Page MenuHomePhabricator

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/Applications/PluginGenerator/ProjectTemplate/CMakeLists.txt b/Applications/PluginGenerator/ProjectTemplate/CMakeLists.txt
index 572d4ebd54..f7afae7f5f 100644
--- a/Applications/PluginGenerator/ProjectTemplate/CMakeLists.txt
+++ b/Applications/PluginGenerator/ProjectTemplate/CMakeLists.txt
@@ -1,329 +1,329 @@
cmake_minimum_required(VERSION 3.18 FATAL_ERROR)
# Change project and application name to your own
set(MY_PROJECT_NAME $(project-name))
set(MY_APP_NAME $(project-app-name))
#-----------------------------------------------------------------------------
-# Set the language standard (MITK requires C++14)
+# Set the language standard (MITK requires C++17)
#-----------------------------------------------------------------------------
-set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED 1)
set(CMAKE_CXX_EXTENSIONS 0)
#-----------------------------------------------------------------------------
# Set a default build type if none was specified
#-----------------------------------------------------------------------------
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to 'Debug' as none was specified.")
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE)
# Set the possible values of build type for cmake-gui
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY
STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
#-----------------------------------------------------------------------------
# Superbuild Option - Enabled by default
#-----------------------------------------------------------------------------
option(${MY_PROJECT_NAME}_USE_SUPERBUILD "Build ${MY_PROJECT_NAME} and the projects it depends on via SuperBuild.cmake." ON)
if(${MY_PROJECT_NAME}_USE_SUPERBUILD)
project(${MY_PROJECT_NAME}-superbuild)
set(${MY_PROJECT_NAME}_SOURCE_DIR ${PROJECT_SOURCE_DIR})
set(${MY_PROJECT_NAME}_BINARY_DIR ${PROJECT_BINARY_DIR})
else()
project(${MY_PROJECT_NAME})
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
CMP0020 # NEW: Automatically link Qt executables to qtmain target on Windows.
CMP0028 # NEW: Double colon in target name means ALIAS or IMPORTED target.
)
foreach(policy ${project_policies})
if(POLICY ${policy})
cmake_policy(SET ${policy} NEW)
endif()
endforeach()
#-----------------------------------------------------------------------------
# Update CMake module path
#------------------------------------------------------------------------------
set(CMAKE_MODULE_PATH
${${MY_PROJECT_NAME}_SOURCE_DIR}/CMake
${CMAKE_MODULE_PATH}
)
#-----------------------------------------------------------------------------
# CMake Function(s) and Macro(s)
#-----------------------------------------------------------------------------
include(CTest)
include(MacroEmptyExternalProject)
#-----------------------------------------------------------------------------
# Output directories.
#-----------------------------------------------------------------------------
foreach(type LIBRARY RUNTIME ARCHIVE)
set(output_dir ${${MY_PROJECT_NAME}_BINARY_DIR}/bin)
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 Options (also shown during superbuild)
#-----------------------------------------------------------------------------
option(BUILD_SHARED_LIBS "Build ${MY_PROJECT_NAME} with shared libraries" ON)
option(WITH_COVERAGE "Enable/Disable coverage" OFF)
option(BUILD_TESTING "Test the project" ON)
option(${MY_PROJECT_NAME}_BUILD_ALL_PLUGINS "Build all ${MY_PROJECT_NAME} plugins" OFF)
mark_as_advanced(${MY_PROJECT_NAME}_INSTALL_RPATH_RELATIVE
${MY_PROJECT_NAME}_BUILD_ALL_PLUGINS
)
#-----------------------------------------------------------------------------
# Superbuild script
#-----------------------------------------------------------------------------
if(${MY_PROJECT_NAME}_USE_SUPERBUILD)
include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake")
return()
endif()
#*****************************************************************************
#**************************** END OF SUPERBUILD ****************************
#*****************************************************************************
#-----------------------------------------------------------------------------
# Prerequesites
#-----------------------------------------------------------------------------
set(${PROJECT_NAME}_MODULES_PACKAGE_DEPENDS_DIR "${PROJECT_SOURCE_DIR}/CMake/PackageDepends")
set(MODULES_PACKAGE_DEPENDS_DIRS ${${PROJECT_NAME}_MODULES_PACKAGE_DEPENDS_DIR})
find_package(MITK 2018.04.99 REQUIRED)
if(COMMAND mitkFunctionCheckMitkCompatibility)
mitkFunctionCheckMitkCompatibility(VERSIONS MITK_VERSION_PLUGIN_SYSTEM 1 REQUIRED)
else()
message(SEND_ERROR "Your MITK version is too old. Please use Git hash b86bf28 or newer")
endif()
link_directories(${MITK_LINK_DIRECTORIES})
#-----------------------------------------------------------------------------
# CMake Function(s) and Macro(s)
#-----------------------------------------------------------------------------
set(CMAKE_MODULE_PATH
${MITK_SOURCE_DIR}/CMake
${CMAKE_MODULE_PATH}
)
include(mitkFunctionCheckCompilerFlags)
include(mitkFunctionGetGccVersion)
include(mitkFunctionGetVersion)
#-----------------------------------------------------------------------------
# Set project specific options and variables (NOT available during superbuild)
#-----------------------------------------------------------------------------
set(${PROJECT_NAME}_VERSION_MAJOR "0")
set(${PROJECT_NAME}_VERSION_MINOR "1")
set(${PROJECT_NAME}_VERSION_PATCH "1")
set(${PROJECT_NAME}_VERSION_STRING "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH}")
# Ask the user if a console window should be shown with the applications
option(${PROJECT_NAME}_SHOW_CONSOLE_WINDOW "Use this to enable or disable the console window when starting GUI Applications" ON)
mark_as_advanced(${PROJECT_NAME}_SHOW_CONSOLE_WINDOW)
if(NOT UNIX)
set(MITK_WIN32_FORCE_STATIC "STATIC")
endif()
#-----------------------------------------------------------------------------
# Get project version info
#-----------------------------------------------------------------------------
mitkFunctionGetVersion(${PROJECT_SOURCE_DIR} ${PROJECT_NAME})
#-----------------------------------------------------------------------------
# Installation preparation
#
# These should be set before any MITK install macros are used
#-----------------------------------------------------------------------------
# on macOS all CTK plugins get copied into every
# application bundle (.app directory) specified here
set(MACOSX_BUNDLE_NAMES)
if(APPLE)
list(APPEND MACOSX_BUNDLE_NAMES ${MY_APP_NAME})
endif(APPLE)
#-----------------------------------------------------------------------------
# Set symbol visibility Flags
#-----------------------------------------------------------------------------
if(CMAKE_COMPILER_IS_GNUCXX)
# The MITK module build system does not yet support default hidden visibility
set(VISIBILITY_CXX_FLAGS ) # "-fvisibility=hidden -fvisibility-inlines-hidden")
endif()
#-----------------------------------------------------------------------------
# Set coverage Flags
#-----------------------------------------------------------------------------
if(WITH_COVERAGE)
if(CMAKE_COMPILER_IS_GNUCXX)
set(coverage_flags "-g -fprofile-arcs -ftest-coverage -O0 -DNDEBUG")
set(COVERAGE_CXX_FLAGS ${coverage_flags})
set(COVERAGE_C_FLAGS ${coverage_flags})
endif()
endif()
#-----------------------------------------------------------------------------
# Project C/CXX Flags
#-----------------------------------------------------------------------------
set(${PROJECT_NAME}_C_FLAGS "${MITK_C_FLAGS} ${COVERAGE_C_FLAGS}")
set(${PROJECT_NAME}_C_FLAGS_DEBUG ${MITK_C_FLAGS_DEBUG})
set(${PROJECT_NAME}_C_FLAGS_RELEASE ${MITK_C_FLAGS_RELEASE})
set(${PROJECT_NAME}_CXX_FLAGS "${MITK_CXX_FLAGS} ${VISIBILITY_CXX_FLAGS} ${COVERAGE_CXX_FLAGS}")
set(${PROJECT_NAME}_CXX_FLAGS_DEBUG ${MITK_CXX_FLAGS_DEBUG})
set(${PROJECT_NAME}_CXX_FLAGS_RELEASE ${MITK_CXX_FLAGS_RELEASE})
set(${PROJECT_NAME}_EXE_LINKER_FLAGS ${MITK_EXE_LINKER_FLAGS})
set(${PROJECT_NAME}_SHARED_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS})
set(${PROJECT_NAME}_MODULE_LINKER_FLAGS ${MITK_MODULE_LINKER_FLAGS})
#-----------------------------------------------------------------------------
# Set C/CXX Flags
#-----------------------------------------------------------------------------
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${${PROJECT_NAME}_C_FLAGS}")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${${PROJECT_NAME}_C_FLAGS_DEBUG}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${${PROJECT_NAME}_C_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${${PROJECT_NAME}_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${${PROJECT_NAME}_CXX_FLAGS_DEBUG}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${${PROJECT_NAME}_CXX_FLAGS_RELEASE}")
set(CMAKE_EXE_LINKER_FLAGS ${${PROJECT_NAME}_EXE_LINKER_FLAGS})
set(CMAKE_SHARED_LINKER_FLAGS ${${PROJECT_NAME}_SHARED_LINKER_FLAGS})
set(CMAKE_MODULE_LINKER_FLAGS ${${PROJECT_NAME}_MODULE_LINKER_FLAGS})
#-----------------------------------------------------------------------------
# Testing
#-----------------------------------------------------------------------------
if(BUILD_TESTING)
# Configuration for the CMake-generated test driver
set(CMAKE_TESTDRIVER_EXTRA_INCLUDES "#include <stdexcept>")
set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN "
try
{")
set(CMAKE_TESTDRIVER_AFTER_TESTMAIN "
}
catch (const std::exception& e)
{
fprintf(stderr, \"%s\\n\", e.what());
return EXIT_FAILURE;
}
catch (...)
{
printf(\"Exception caught in the test driver\\n\");
return EXIT_FAILURE;
}")
endif()
#-----------------------------------------------------------------------------
# ${MY_PROJECT_NAME}_SUPERBUILD_BINARY_DIR
#-----------------------------------------------------------------------------
# If ${MY_PROJECT_NAME}_SUPERBUILD_BINARY_DIR isn't defined, it means this project is
# *NOT* build using Superbuild. In that specific case, ${MY_PROJECT_NAME}_SUPERBUILD_BINARY_DIR
# should default to PROJECT_BINARY_DIR
if(NOT DEFINED ${PROJECT_NAME}_SUPERBUILD_BINARY_DIR)
set(${PROJECT_NAME}_SUPERBUILD_BINARY_DIR ${PROJECT_BINARY_DIR})
endif()
#-----------------------------------------------------------------------------
# MITK modules
#-----------------------------------------------------------------------------
#add_subdirectory(Modules)
#-----------------------------------------------------------------------------
# CTK plugins
#-----------------------------------------------------------------------------
# The CMake code in this section *must* be in the top-level CMakeLists.txt file
macro(GetMyTargetLibraries all_target_libraries varname)
set(re_ctkplugin "^$(project-plugin-base)_[a-zA-Z0-9_]+$")
set(_tmp_list)
list(APPEND _tmp_list ${all_target_libraries})
ctkMacroListFilter(_tmp_list re_ctkplugin OUTPUT_VARIABLE ${varname})
endmacro()
include(${CMAKE_CURRENT_SOURCE_DIR}/Plugins/Plugins.cmake)
ctkMacroSetupPlugins(${PROJECT_PLUGINS}
BUILD_OPTION_PREFIX ${MY_PROJECT_NAME}_
BUILD_ALL ${${MY_PROJECT_NAME}_BUILD_ALL_PLUGINS})
#-----------------------------------------------------------------------------
# Add subdirectories
#-----------------------------------------------------------------------------
add_subdirectory(Apps/$(project-app-name))
#-----------------------------------------------------------------------------
# Installation
#-----------------------------------------------------------------------------
# set MITK cpack variables
include(mitkSetupCPack)
# Customize CPack variables for this project
include(CPackSetup)
list(APPEND CPACK_CREATE_DESKTOP_LINKS "${MY_APP_NAME}")
configure_file(${MITK_SOURCE_DIR}/MITKCPackOptions.cmake.in
${PROJECT_BINARY_DIR}/${PROJECT_NAME}CPackOptions.cmake @ONLY)
set(CPACK_PROJECT_CONFIG_FILE "${PROJECT_BINARY_DIR}/${PROJECT_NAME}CPackOptions.cmake")
# include CPack model once all variables are set
include(CPack)
# Additional installation rules
include(mitkInstallRules)
#-----------------------------------------------------------------------------
# Last configuration steps
#-----------------------------------------------------------------------------
diff --git a/CMake/PackageDepends/MITK_GDCM_Config.cmake b/CMake/PackageDepends/MITK_GDCM_Config.cmake
index 0e26d2dd63..3a85869601 100644
--- a/CMake/PackageDepends/MITK_GDCM_Config.cmake
+++ b/CMake/PackageDepends/MITK_GDCM_Config.cmake
@@ -1,2 +1,10 @@
-list(APPEND ALL_INCLUDE_DIRECTORIES ${GDCM_INCLUDE_DIRS})
-list(APPEND ALL_LIBRARIES ${GDCM_LIBRARIES})
+foreach(gdcm_component ${GDCM_REQUIRED_COMPONENTS_BY_MODULE})
+ if(NOT gdcm_component MATCHES "^gdcm")
+ set(gdcm_component "gdcm${gdcm_component}")
+ endif()
+ list(APPEND _gdcm_required_components_by_module ${gdcm_component})
+endforeach()
+
+find_package(GDCM COMPONENTS ${_gdcm_required_components_by_module} REQUIRED)
+
+list(APPEND ALL_LIBRARIES ${_gdcm_required_components_by_module})
diff --git a/CMake/mitkFunctionCreateModule.cmake b/CMake/mitkFunctionCreateModule.cmake
index f7b334c80b..cfb328b7b4 100644
--- a/CMake/mitkFunctionCreateModule.cmake
+++ b/CMake/mitkFunctionCreateModule.cmake
@@ -1,651 +1,647 @@
##################################################################
#
# mitk_create_module
#
#! Creates a module for the automatic module dependency system within MITK.
#!
#! Example:
#!
#! \code
#! mitk_create_module(
#! DEPENDS PUBLIC MitkCore
#! PACKAGE_DEPENDS
#! PRIVATE Qt5|Xml+Networking
#! PUBLIC ITK|Watersheds
#! \endcode
#!
#! The <moduleName> parameter specifies the name of the module which is used
#! to create a logical target name. The parameter is optional in case the
#! MITK_MODULE_NAME_DEFAULTS_TO_DIRECTORY_NAME variable evaluates to TRUE. The
#! module name will then be derived from the directory name in which this
#! function is called.
#!
#! If set, the following variables will be used to validate the module name:
#!
#! MITK_MODULE_NAME_REGEX_MATCH The module name must match this regular expression.
#! MITK_MODULE_NAME_REGEX_NOT_MATCH The module name must not match this regular expression.
#!
#! If the MITK_MODULE_NAME_PREFIX variable is set, the module name will be prefixed
#! with its contents.
#!
#! A modules source files are specified in a separate CMake file usually
#! called files.cmake, located in the module root directory. The
#! mitk_create_module() macro evaluates the following CMake variables
#! from the files.cmake file:
#!
#! - CPP_FILES A list of .cpp files
#! - H_FILES A list of .h files without a corresponding .cpp file
#! - TXX_FILES A list of .txx files
#! - RESOURCE_FILES A list of files (resources) which are embedded into the module
#! - MOC_H_FILES A list of Qt header files which should be processed by the MOC
#! - UI_FILES A list of .ui Qt UI files
#! - QRC_FILES A list of .qrc Qt resource files
#! - DOX_FILES A list of .dox Doxygen files
#!
#! List of variables available after the function is called:
#! - MODULE_NAME
#! - MODULE_TARGET
#! - MODULE_IS_ENABLED
#!
#! \sa mitk_create_executable
#!
#! Parameters (all optional):
#!
#! \param <moduleName> The module name (also used as target name)
#! \param FILES_CMAKE File name of a CMake file setting source list variables
#! (defaults to files.cmake)
#! \param VERSION Module version number, e.g. "1.2.0"
#! \param AUTOLOAD_WITH A module target name identifying the module which will
#! trigger the automatic loading of this module
#! \param DEPRECATED_SINCE Marks this modules as deprecated since <arg>
#! \param DESCRIPTION A description for this module
#!
#! Multi-value Parameters (all optional):
#!
#! \param INCLUDE_DIRS Include directories for this module:
#! \verbatim
#! [[PUBLIC|PRIVATE|INTERFACE] <dir1>...]...
#! \endverbatim
#! The default scope for include directories is PUBLIC.
#! \param DEPENDS List of module dependencies:
#! \verbatim
#! [[PUBLIC|PRIVATE|INTERFACE] <module1>...]...
#! \endverbatim
#! The default scope for module dependencies is PUBLIC.
#! \param PACKAGE_DEPENDS List of public packages dependencies (e.g. Qt, VTK, etc.).
#! Package dependencies have the following syntax:
#! \verbatim
#! [PUBLIC|PRIVATE|INTERFACE] PACKAGE[|COMPONENT1[+COMPONENT2]...]
#! \endverbatim
#! The default scope for package dependencies is PRIVATE.
#! \param ADDITIONAL_LIBS List of additional private libraries linked to this module.
#! The folder containing the library will be added to the global list of library search paths.
#! \param CPP_FILES List of source files for this module. If the list is non-empty,
#! the module does not need to provide a files.cmake file or FILES_CMAKE argument.
#! \param H_FILES List of public header files for this module. It is recommended to use
#! a files.cmake file instead.
#!
#! Options (optional)
#!
#! \param FORCE_STATIC Force building this module as a static library
#! \param GCC_DEFAULT_VISIBILITY Do not use gcc visibility flags - all
#! symbols will be exported
#! \param NO_INIT Do not create CppMicroServices initialization code
#! \param NO_FEATURE_INFO Do not create a feature info by calling add_feature_info()
#! \param WARNINGS_NO_ERRORS Do not treat compiler warnings as errors
#
##################################################################
function(mitk_create_module)
set(_macro_params
VERSION # module version number, e.g. "1.2.0"
EXPORT_DEFINE # export macro name for public symbols of this module (DEPRECATED)
AUTOLOAD_WITH # a module target name identifying the module which will trigger the
# automatic loading of this module
FILES_CMAKE # file name of a CMake file setting source list variables
# (defaults to files.cmake)
DEPRECATED_SINCE # marks this modules as deprecated
DESCRIPTION # a description for this module
)
set(_macro_multiparams
SUBPROJECTS # list of CDash labels (deprecated)
INCLUDE_DIRS # include directories: [PUBLIC|PRIVATE|INTERFACE] <list>
INTERNAL_INCLUDE_DIRS # include dirs internal to this module (DEPRECATED)
DEPENDS # list of modules this module depends on: [PUBLIC|PRIVATE|INTERFACE] <list>
DEPENDS_INTERNAL # list of modules this module internally depends on (DEPRECATED)
PACKAGE_DEPENDS # list of "packages this module depends on (e.g. Qt, VTK, etc.): [PUBLIC|PRIVATE|INTERFACE] <package-list>
TARGET_DEPENDS # list of CMake targets this module should depend on: [PUBLIC|PRIVATE|INTERFACE] <list>
ADDITIONAL_LIBS # list of addidtional private libraries linked to this module.
CPP_FILES # list of cpp files
H_FILES # list of header files: [PUBLIC|PRIVATE] <list>
)
set(_macro_options
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_DEFAULT_INCLUDE_DIRS # do not add default include directories like "include" or "."
NO_INIT # do not create CppMicroServices initialization code
NO_FEATURE_INFO # do not create a feature info by calling add_feature_info()
WARNINGS_NO_ERRORS # do not treat compiler warnings as errors
EXECUTABLE # create an executable; do not use directly, use mitk_create_executable() instead
C_MODULE # compile all source files as C sources
CXX_MODULE # compile all source files as C++ sources
)
cmake_parse_arguments(MODULE "${_macro_options}" "${_macro_params}" "${_macro_multiparams}" ${ARGN})
set(MODULE_NAME ${MODULE_UNPARSED_ARGUMENTS})
# -----------------------------------------------------------------
# Sanity checks
if(NOT MODULE_NAME)
if(MITK_MODULE_NAME_DEFAULTS_TO_DIRECTORY_NAME)
get_filename_component(MODULE_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
else()
message(SEND_ERROR "The module name must not be empty")
endif()
endif()
set(_deprecated_args INTERNAL_INCLUDE_DIRS DEPENDS_INTERNAL EXPORT_DEFINE HEADERS_ONLY)
foreach(_deprecated_arg ${_deprecated_args})
if(MODULE_${_deprecated_arg})
message(WARNING "The ${_deprecated_arg} argument is deprecated")
endif()
endforeach()
set(_module_type module)
set(_Module_type Module)
if(MODULE_EXECUTABLE)
set(_module_type executable)
set(_Module_type Executable)
endif()
if(MITK_MODULE_NAME_REGEX_MATCH)
if(NOT ${MODULE_NAME} MATCHES ${MITK_MODULE_NAME_REGEX_MATCH})
message(SEND_ERROR "The ${_module_type} name \"${MODULE_NAME}\" does not match the regular expression \"${MITK_MODULE_NAME_REGEX_MATCH}\".")
endif()
endif()
if(MITK_MODULE_NAME_REGEX_NOT_MATCH)
if(${MODULE_NAME} MATCHES ${MITK_MODULE_NAME_REGEX_NOT_MATCH})
message(SEND_ERROR "The ${_module_type} name \"${MODULE_NAME}\" must not match the regular expression \"${MITK_MODULE_NAME_REGEX_NOT_MATCH}\".")
endif()
endif()
if(MITK_MODULE_NAME_PREFIX AND NOT MODULE_NAME MATCHES "^${MITK_MODULE_NAME_PREFIX}.*$")
set(MODULE_NAME "${MITK_MODULE_NAME_PREFIX}${MODULE_NAME}")
endif()
if(NOT MODULE_FILES_CMAKE)
set(MODULE_FILES_CMAKE files.cmake)
endif()
if(NOT IS_ABSOLUTE ${MODULE_FILES_CMAKE})
set(MODULE_FILES_CMAKE ${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_FILES_CMAKE})
endif()
# -----------------------------------------------------------------
# Check if module should be build
set(MODULE_TARGET ${MODULE_NAME})
# 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)
# first of all we check for the dependencies
_mitk_parse_package_args(${MODULE_PACKAGE_DEPENDS})
mitk_check_module_dependencies(MODULES ${MODULE_DEPENDS}
PACKAGES ${PACKAGE_NAMES}
MISSING_DEPENDENCIES_VAR _MISSING_DEP
PACKAGE_DEPENDENCIES_VAR PACKAGE_NAMES)
if(_MISSING_DEP)
if(MODULE_NO_FEATURE_INFO)
message("${_Module_type} ${MODULE_NAME} won't be built, missing dependency: ${_MISSING_DEP}")
endif()
set(MODULE_IS_ENABLED 0)
else()
foreach(dep ${MODULE_DEPENDS})
if(TARGET ${dep})
get_target_property(AUTLOAD_DEP ${dep} MITK_AUTOLOAD_DIRECTORY)
if (AUTLOAD_DEP)
message(SEND_ERROR "Module \"${MODULE_NAME}\" has an invalid dependency on autoload module \"${dep}\". Check MITK_CREATE_MODULE usage for \"${MODULE_NAME}\".")
endif()
endif()
endforeach(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 ${PACKAGE_NAMES})
if((DEFINED MITK_USE_${_package}) AND NOT (MITK_USE_${_package}))
message("${_Module_type} ${MODULE_NAME} won't be built. Turn on MITK_USE_${_package} if you want to use it.")
set(MODULE_IS_ENABLED 0)
break()
endif()
endforeach()
endif()
endif()
# -----------------------------------------------------------------
# Start creating the module
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_CPP )
set(Q${KITNAME}_GENERATED_MOC_CPP )
set(Q${KITNAME}_GENERATED_QRC_CPP )
set(Q${KITNAME}_GENERATED_UI_CPP )
# 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()
endif()
set(_module_autoload_meta_target "${CMAKE_PROJECT_NAME}-autoload")
# create a meta-target if it does not already exist
if(NOT TARGET ${_module_autoload_meta_target})
add_custom_target(${_module_autoload_meta_target})
set_property(TARGET ${_module_autoload_meta_target} PROPERTY FOLDER "${MITK_ROOT_FOLDER}/Modules/Autoload")
endif()
if(NOT MODULE_EXPORT_DEFINE)
set(MODULE_EXPORT_DEFINE ${MODULE_NAME}_EXPORT)
endif()
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)
if (EXISTS ${MODULE_FILES_CMAKE})
include(${MODULE_FILES_CMAKE})
endif()
if(MODULE_CPP_FILES)
list(APPEND CPP_FILES ${MODULE_CPP_FILES})
endif()
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src")
# Preprend the "src" directory to the cpp file list
set(_cpp_files ${CPP_FILES})
set(CPP_FILES )
foreach(_cpp_file ${_cpp_files})
list(APPEND CPP_FILES "src/${_cpp_file}")
endforeach()
endif()
if(CPP_FILES OR RESOURCE_FILES OR UI_FILES OR MOC_H_FILES OR QRC_FILES)
set(MODULE_HEADERS_ONLY 0)
if(MODULE_C_MODULE)
set_source_files_properties(${CPP_FILES} PROPERTIES LANGUAGE C)
elseif(MODULE_CXX_MODULE)
set_source_files_properties(${CPP_FILES} PROPERTIES LANGUAGE CXX)
endif()
else()
set(MODULE_HEADERS_ONLY 1)
if(MODULE_AUTOLOAD_WITH)
message(SEND_ERROR "A headers only module cannot be auto-loaded")
endif()
endif()
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 OR NOT CMAKE_COMPILER_IS_GNUCXX)
# We only support hidden visibility for gcc for now. Clang 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
set(CMAKE_CXX_VISIBILITY_PRESET default)
set(CMAKE_VISIBILITY_INLINES_HIDDEN 0)
else()
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
endif()
if(NOT MODULE_WARNINGS_NO_ERRORS)
if(MSVC_VERSION)
mitkFunctionCheckCAndCXXCompilerFlags("/WX" module_c_flags module_cxx_flags)
# this would turn on unused parameter warnings, but unfortunately MSVC cannot
# distinguish yet between internal and external headers so this would be triggered
# a lot by external code. There is support for it on the way so this line could be
# reactivated after https://gitlab.kitware.com/cmake/cmake/issues/17904 has been fixed.
# mitkFunctionCheckCAndCXXCompilerFlags("/w34100" 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)
mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=class-memaccess" module_c_flags module_cxx_flags)
mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=inconsistent-missing-override" module_c_flags module_cxx_flags)
mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=deprecated-copy" module_c_flags module_cxx_flags)
mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=cast-function-type" module_c_flags module_cxx_flags)
mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=deprecated-declarations" module_c_flags module_cxx_flags)
mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=type-limits" module_c_flags module_cxx_flags)
endif()
endif()
if(MODULE_FORCE_STATIC)
set(_STATIC STATIC)
else()
set(_STATIC )
endif(MODULE_FORCE_STATIC)
if(NOT MODULE_HEADERS_ONLY)
if(NOT MODULE_NO_INIT OR RESOURCE_FILES)
find_package(CppMicroServices QUIET NO_MODULE REQUIRED)
endif()
if(NOT MODULE_NO_INIT)
usFunctionGenerateModuleInit(CPP_FILES)
endif()
set(binary_res_files )
set(source_res_files )
if(RESOURCE_FILES)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/resource")
set(res_dir resource)
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Resources")
set(res_dir Resources)
else()
message(SEND_ERROR "Resources specified but ${CMAKE_CURRENT_SOURCE_DIR}/resource directory not found.")
endif()
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()
# Add a source level dependencies on resource files
usFunctionGetResourceSource(TARGET ${MODULE_TARGET} OUT CPP_FILES)
endif()
endif()
if(MITK_USE_Qt5)
if(UI_FILES)
qt5_wrap_ui(Q${KITNAME}_GENERATED_UI_CPP ${UI_FILES})
endif()
if(MOC_H_FILES)
qt5_wrap_cpp(Q${KITNAME}_GENERATED_MOC_CPP ${MOC_H_FILES} OPTIONS -DBOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
endif()
if(QRC_FILES)
qt5_add_resources(Q${KITNAME}_GENERATED_QRC_CPP ${QRC_FILES})
endif()
endif()
set(Q${KITNAME}_GENERATED_CPP ${Q${KITNAME}_GENERATED_CPP} ${Q${KITNAME}_GENERATED_UI_CPP} ${Q${KITNAME}_GENERATED_MOC_CPP} ${Q${KITNAME}_GENERATED_QRC_CPP})
mitkFunctionOrganizeSources(
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})
# ---------------------------------------------------------------
# Create the actual module target
if(MODULE_HEADERS_ONLY)
add_library(${MODULE_TARGET} INTERFACE)
# INTERFACE_LIBRARY targets may only have whitelisted properties. The property "FOLDER" is not allowed.
# set_property(TARGET ${MODULE_TARGET} PROPERTY FOLDER "${MITK_ROOT_FOLDER}/Modules")
else()
if(MODULE_EXECUTABLE)
if(MITK_SHOW_CONSOLE_WINDOW)
set(_SHOW_CONSOLE_OPTION "")
else()
set(_SHOW_CONSOLE_OPTION WIN32)
endif()
add_executable(${MODULE_TARGET} ${_SHOW_CONSOLE_OPTION}
${MODULE_CPP_FILES} ${coverage_sources} ${CPP_FILES_GENERATED} ${Q${KITNAME}_GENERATED_CPP}
${DOX_FILES} ${UI_FILES} ${QRC_FILES} ${WINDOWS_ICON_RESOURCE_FILE})
set_property(TARGET ${MODULE_TARGET} PROPERTY FOLDER "${MITK_ROOT_FOLDER}/Modules/Executables")
set(_us_module_name main)
else()
add_library(${MODULE_TARGET} ${_STATIC}
${coverage_sources} ${CPP_FILES_GENERATED} ${Q${KITNAME}_GENERATED_CPP}
${DOX_FILES} ${UI_FILES} ${QRC_FILES})
set_property(TARGET ${MODULE_TARGET} PROPERTY FOLDER "${MITK_ROOT_FOLDER}/Modules")
set(_us_module_name ${MODULE_TARGET})
endif()
# Apply properties to the module target.
target_compile_definitions(${MODULE_TARGET} PRIVATE US_MODULE_NAME=${_us_module_name})
if(MODULE_C_MODULE)
if(module_c_flags)
string(REPLACE " " ";" module_c_flags "${module_c_flags}")
target_compile_options(${MODULE_TARGET} PRIVATE ${module_c_flags})
endif()
if(module_c_flags_debug)
string(REPLACE " " ";" module_c_flags_debug "${module_c_flags_debug}")
target_compile_options(${MODULE_TARGET} PRIVATE $<$<CONFIG:Debug>:${module_c_flags_debug}>)
endif()
if(module_c_flags_release)
string(REPLACE " " ";" module_c_flags_release "${module_c_flags_release}")
target_compile_options(${MODULE_TARGET} PRIVATE $<$<CONFIG:Release>:${module_c_flags_release}>)
endif()
else()
if(module_cxx_flags)
string(REPLACE " " ";" module_cxx_flags "${module_cxx_flags}")
target_compile_options(${MODULE_TARGET} PRIVATE ${module_cxx_flags})
endif()
if(module_cxx_flags_debug)
string(REPLACE " " ";" module_cxx_flags_debug "${module_cxx_flags_debug}")
target_compile_options(${MODULE_TARGET} PRIVATE $<$<CONFIG:Debug>:${module_cxx_flags_debug}>)
endif()
if(module_cxx_flags_release)
string(REPLACE " " ";" module_cxx_flags_release "${module_cxx_flags_release}")
target_compile_options(${MODULE_TARGET} PRIVATE $<$<CONFIG:Release>:${module_cxx_flags_release}>)
endif()
endif()
set_property(TARGET ${MODULE_TARGET} PROPERTY US_MODULE_NAME ${_us_module_name})
# 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)
target_link_libraries(${MODULE_TARGET} PRIVATE ${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_TARGET})
if(MODULE_AUTOLOAD_WITH)
# for auto-loaded modules, adapt the output directory
add_dependencies(${_module_autoload_meta_target} ${MODULE_TARGET})
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_TARGET} PROPERTIES
${_module_output_prop} ${_module_output_dir}/${MODULE_AUTOLOAD_WITH})
else()
set_target_properties(${MODULE_TARGET} PROPERTIES
${_module_output_prop} ${CMAKE_${_module_output_prop}}/${MODULE_AUTOLOAD_WITH})
endif()
set_target_properties(${MODULE_TARGET} 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_TARGET})
endif()
if(binary_res_files)
usFunctionAddResources(TARGET ${MODULE_TARGET}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${res_dir}
FILES ${binary_res_files})
endif()
if(source_res_files)
usFunctionAddResources(TARGET ${MODULE_TARGET}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${res_dir}
FILES ${source_res_files})
endif()
if(binary_res_files OR source_res_files)
usFunctionEmbedResources(TARGET ${MODULE_TARGET})
endif()
if(MODULE_DEPRECATED_SINCE)
set_property(TARGET ${MODULE_TARGET} PROPERTY MITK_MODULE_DEPRECATED_SINCE ${MODULE_DEPRECATED_SINCE})
endif()
# create export macros
if (NOT MODULE_EXECUTABLE)
set(_export_macro_name )
if(MITK_LEGACY_EXPORT_MACRO_NAME)
set(_export_macro_names
EXPORT_MACRO_NAME ${MODULE_EXPORT_DEFINE}
NO_EXPORT_MACRO_NAME ${MODULE_NAME}_NO_EXPORT
DEPRECATED_MACRO_NAME ${MODULE_NAME}_DEPRECATED
NO_DEPRECATED_MACRO_NAME ${MODULE_NAME}_NO_DEPRECATED
)
endif()
generate_export_header(${MODULE_NAME}
${_export_macro_names}
EXPORT_FILE_NAME ${MODULE_NAME}Exports.h
)
endif()
target_include_directories(${MODULE_TARGET} PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
endif()
# ---------------------------------------------------------------
# Properties for both header-only and compiled modules
if(MODULE_HEADERS_ONLY)
set(_module_property_type INTERFACE)
else()
set(_module_property_type PUBLIC)
endif()
if(MODULE_TARGET_DEPENDS)
target_link_libraries(${MODULE_TARGET} ${MODULE_TARGET_DEPENDS})
endif()
set(DEPENDS "${MODULE_DEPENDS}")
if(NOT MODULE_NO_INIT AND NOT MODULE_HEADERS_ONLY)
# Add a CppMicroServices dependency implicitly, since it is
# needed for the generated "module initialization" code.
set(DEPENDS "CppMicroServices;${DEPENDS}")
endif()
if(DEPENDS OR MODULE_PACKAGE_DEPENDS)
mitk_use_modules(TARGET ${MODULE_TARGET}
MODULES ${DEPENDS}
PACKAGES ${MODULE_PACKAGE_DEPENDS}
)
endif()
- if(NOT MODULE_C_MODULE)
- target_compile_features(${MODULE_TARGET} ${_module_property_type} ${MITK_CXX_FEATURES})
- endif()
-
# add include directories
if(MODULE_INTERNAL_INCLUDE_DIRS)
target_include_directories(${MODULE_TARGET} PRIVATE ${MODULE_INTERNAL_INCLUDE_DIRS})
endif()
if(NOT MODULE_NO_DEFAULT_INCLUDE_DIRS)
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_include_directories(${MODULE_TARGET} ${_module_property_type} include)
else()
target_include_directories(${MODULE_TARGET} ${_module_property_type} .)
endif()
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_include_directories(${MODULE_TARGET} PRIVATE src)
endif()
endif()
target_include_directories(${MODULE_TARGET} ${_module_property_type} ${MODULE_INCLUDE_DIRS})
endif()
# -----------------------------------------------------------------
# Record missing dependency information
if(_MISSING_DEP)
if(MODULE_DESCRIPTION)
set(MODULE_DESCRIPTION "${MODULE_DESCRIPTION} (missing dependencies: ${_MISSING_DEP})")
else()
set(MODULE_DESCRIPTION "(missing dependencies: ${_MISSING_DEP})")
endif()
endif()
if(NOT MODULE_NO_FEATURE_INFO)
add_feature_info(${MODULE_NAME} MODULE_IS_ENABLED "${MODULE_DESCRIPTION}")
endif()
set(MODULE_NAME ${MODULE_NAME} PARENT_SCOPE)
set(MODULE_TARGET ${MODULE_TARGET} PARENT_SCOPE)
set(MODULE_IS_ENABLED ${MODULE_IS_ENABLED} PARENT_SCOPE)
endfunction()
diff --git a/CMakeExternals/ANN.cmake b/CMakeExternals/ANN.cmake
index fc9e20ab75..25822784e8 100644
--- a/CMakeExternals/ANN.cmake
+++ b/CMakeExternals/ANN.cmake
@@ -1,53 +1,56 @@
#-----------------------------------------------------------------------------
# ANN
#-----------------------------------------------------------------------------
if(MITK_USE_ANN)
# Sanity checks
if(DEFINED ANN_DIR AND NOT EXISTS ${ANN_DIR})
message(FATAL_ERROR "ANN_DIR variable is defined but corresponds to non-existing directory")
endif()
set(proj ANN)
set(proj_DEPENDENCIES )
set(ANN_DEPENDS ${proj})
if(NOT DEFINED ANN_DIR)
set(additional_args )
if(CTEST_USE_LAUNCHERS)
list(APPEND additional_args
"-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake"
)
endif()
- set(patch_cmd ${CMAKE_COMMAND} -Dproj:STRING=${proj} -Dproj_target:STRING=ann -P ${CMAKE_CURRENT_LIST_DIR}/GenerateDefaultCMakeBuildSystem.cmake)
+ set(patch_cmd
+ ${CMAKE_COMMAND} -Dproj:STRING=${proj} -Dproj_target:STRING=ann -P ${CMAKE_CURRENT_LIST_DIR}/GenerateDefaultCMakeBuildSystem.cmake
+ COMMAND ${PATCH_COMMAND} -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/ANN.patch
+ )
ExternalProject_Add(${proj}
LIST_SEPARATOR ${sep}
URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/ann_1.1.2.tar.gz
URL_MD5 7ffaacc7ea79ca39d4958a6378071365
PATCH_COMMAND ${patch_cmd}
CMAKE_GENERATOR ${gen}
CMAKE_GENERATOR_PLATFORM ${gen_platform}
CMAKE_ARGS
${ep_common_args}
${additional_args}
CMAKE_CACHE_ARGS
${ep_common_cache_args}
CMAKE_CACHE_DEFAULT_ARGS
${ep_common_cache_default_args}
DEPENDS ${proj_DEPENDENCIES}
)
set(ANN_DIR ${ep_prefix}/lib/cmake/ANN)
mitkFunctionInstallExternalCMakeProject(${proj})
else()
mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}")
endif()
endif()
diff --git a/CMakeExternals/ANN.patch b/CMakeExternals/ANN.patch
new file mode 100644
index 0000000000..fb191edf81
--- /dev/null
+++ b/CMakeExternals/ANN.patch
@@ -0,0 +1,148 @@
+diff -urNb ann_1.1.2/src/ANN.cpp ANN/src/ANN.cpp
+--- ann_1.1.2/src/ANN.cpp 2010-01-28 05:40:01.000000000 +0100
++++ ANN/src/ANN.cpp 2022-03-17 21:55:57.011720500 +0100
+@@ -48,9 +48,9 @@
+ ANNpoint p,
+ ANNpoint q)
+ {
+- register int d;
+- register ANNcoord diff;
+- register ANNcoord dist;
++ int d;
++ ANNcoord diff;
++ ANNcoord dist;
+
+ dist = 0;
+ for (d = 0; d < dim; d++) {
+diff -urNb ann_1.1.2/src/kd_fix_rad_search.cpp ANN/src/kd_fix_rad_search.cpp
+--- ann_1.1.2/src/kd_fix_rad_search.cpp 2010-01-28 05:40:01.000000000 +0100
++++ ANN/src/kd_fix_rad_search.cpp 2022-03-17 21:56:09.257624400 +0100
+@@ -147,11 +147,11 @@
+
+ void ANNkd_leaf::ann_FR_search(ANNdist box_dist)
+ {
+- register ANNdist dist; // distance to data point
+- register ANNcoord* pp; // data coordinate pointer
+- register ANNcoord* qq; // query coordinate pointer
+- register ANNcoord t;
+- register int d;
++ ANNdist dist; // distance to data point
++ ANNcoord* pp; // data coordinate pointer
++ ANNcoord* qq; // query coordinate pointer
++ ANNcoord t;
++ int d;
+
+ for (int i = 0; i < n_pts; i++) { // check points in bucket
+
+diff -urNb ann_1.1.2/src/kd_pr_search.cpp ANN/src/kd_pr_search.cpp
+--- ann_1.1.2/src/kd_pr_search.cpp 2010-01-28 05:40:01.000000000 +0100
++++ ANN/src/kd_pr_search.cpp 2022-03-17 21:56:20.260622500 +0100
+@@ -180,12 +180,12 @@
+
+ void ANNkd_leaf::ann_pri_search(ANNdist box_dist)
+ {
+- register ANNdist dist; // distance to data point
+- register ANNcoord* pp; // data coordinate pointer
+- register ANNcoord* qq; // query coordinate pointer
+- register ANNdist min_dist; // distance to k-th closest point
+- register ANNcoord t;
+- register int d;
++ ANNdist dist; // distance to data point
++ ANNcoord* pp; // data coordinate pointer
++ ANNcoord* qq; // query coordinate pointer
++ ANNdist min_dist; // distance to k-th closest point
++ ANNcoord t;
++ int d;
+
+ min_dist = ANNprPointMK->max_key(); // k-th smallest distance so far
+
+diff -urNb ann_1.1.2/src/kd_search.cpp ANN/src/kd_search.cpp
+--- ann_1.1.2/src/kd_search.cpp 2010-01-28 05:40:01.000000000 +0100
++++ ANN/src/kd_search.cpp 2022-03-17 21:56:28.132124600 +0100
+@@ -171,12 +171,12 @@
+
+ void ANNkd_leaf::ann_search(ANNdist box_dist)
+ {
+- register ANNdist dist; // distance to data point
+- register ANNcoord* pp; // data coordinate pointer
+- register ANNcoord* qq; // query coordinate pointer
+- register ANNdist min_dist; // distance to k-th closest point
+- register ANNcoord t;
+- register int d;
++ ANNdist dist; // distance to data point
++ ANNcoord* pp; // data coordinate pointer
++ ANNcoord* qq; // query coordinate pointer
++ ANNdist min_dist; // distance to k-th closest point
++ ANNcoord t;
++ int d;
+
+ min_dist = ANNkdPointMK->max_key(); // k-th smallest distance so far
+
+diff -urNb ann_1.1.2/src/kd_util.cpp ANN/src/kd_util.cpp
+--- ann_1.1.2/src/kd_util.cpp 2010-01-28 05:40:01.000000000 +0100
++++ ANN/src/kd_util.cpp 2022-03-17 21:56:34.316330300 +0100
+@@ -127,10 +127,10 @@
+ const ANNpoint hi, // high point of box
+ int dim) // dimension of space
+ {
+- register ANNdist dist = 0.0; // sum of squared distances
+- register ANNdist t;
++ ANNdist dist = 0.0; // sum of squared distances
++ ANNdist t;
+
+- for (register int d = 0; d < dim; d++) {
++ for (int d = 0; d < dim; d++) {
+ if (q[d] < lo[d]) { // q is left of box
+ t = ANNdist(lo[d]) - ANNdist(q[d]);
+ dist = ANN_SUM(dist, ANN_POW(t));
+@@ -238,8 +238,8 @@
+ int l = 0; // left end of current subarray
+ int r = n-1; // right end of current subarray
+ while (l < r) {
+- register int i = (r+l)/2; // select middle as pivot
+- register int k;
++ int i = (r+l)/2; // select middle as pivot
++ int k;
+
+ if (PA(i,d) > PA(r,d)) // make sure last > pivot
+ PASWAP(i,r)
+diff -urNb ann_1.1.2/src/pr_queue.h ANN/src/pr_queue.h
+--- ann_1.1.2/src/pr_queue.h 2010-01-28 05:40:01.000000000 +0100
++++ ANN/src/pr_queue.h 2022-03-17 21:56:44.266327900 +0100
+@@ -86,9 +86,9 @@
+ PQinfo inf) // item info
+ {
+ if (++n > max_size) annError("Priority queue overflow.", ANNabort);
+- register int r = n;
++ int r = n;
+ while (r > 1) { // sift up new item
+- register int p = r/2;
++ int p = r/2;
+ ANN_FLOP(1) // increment floating ops
+ if (pq[p].key <= kv) // in proper order
+ break;
+@@ -105,9 +105,9 @@
+ {
+ kv = pq[1].key; // key of min item
+ inf = pq[1].info; // information of min item
+- register PQkey kn = pq[n--].key;// last item in queue
+- register int p = 1; // p points to item out of position
+- register int r = p<<1; // left child of p
++ PQkey kn = pq[n--].key;// last item in queue
++ int p = 1; // p points to item out of position
++ int r = p<<1; // left child of p
+ while (r <= n) { // while r is still within the heap
+ ANN_FLOP(2) // increment floating ops
+ // set r to smaller child of p
+diff -urNb ann_1.1.2/src/pr_queue_k.h ANN/src/pr_queue_k.h
+--- ann_1.1.2/src/pr_queue_k.h 2010-01-28 05:40:01.000000000 +0100
++++ ANN/src/pr_queue_k.h 2022-03-17 21:56:55.450345900 +0100
+@@ -100,7 +100,7 @@
+ PQKkey kv, // key value
+ PQKinfo inf) // item info
+ {
+- register int i;
++ int i;
+ // slide larger values up
+ for (i = n; i > 0; i--) {
+ if (mk[i-1].key > kv)
diff --git a/CMakeExternals/Boost.cmake b/CMakeExternals/Boost.cmake
index 2a92b4030c..9c086d7ef2 100644
--- a/CMakeExternals/Boost.cmake
+++ b/CMakeExternals/Boost.cmake
@@ -1,342 +1,342 @@
#-----------------------------------------------------------------------------
# Boost
#-----------------------------------------------------------------------------
include(mitkFunctionGetMSVCVersion)
#[[ Sanity checks ]]
if(DEFINED BOOST_ROOT AND NOT EXISTS ${BOOST_ROOT})
message(FATAL_ERROR "BOOST_ROOT variable is defined but corresponds to non-existing directory")
endif()
string(REPLACE "^^" ";" MITK_USE_Boost_LIBRARIES "${MITK_USE_Boost_LIBRARIES}")
set(proj Boost)
set(proj_DEPENDENCIES )
set(Boost_DEPENDS ${proj})
if(NOT DEFINED BOOST_ROOT AND NOT MITK_USE_SYSTEM_Boost)
#[[ Reset variables. ]]
set(patch_cmd "")
set(configure_cmd "")
set(install_cmd "")
set(BOOST_ROOT ${ep_prefix})
if(WIN32)
set(BOOST_LIBRARYDIR "${BOOST_ROOT}/lib")
endif()
#[[ If you update Boost, make sure that the FindBoost module of the minimum
required version of CMake supports the new version of Boost.
In case you are using a higher version of CMake, download at least the
source code of the minimum required version of CMake to look into the
right version of the FindBoost module:
<CMAKE_INSTALL_DIR>/share/cmake-<VERSION>/Modules/FindBoost.cmake
Search for a list called _Boost_KNOWN_VERSIONS. If the new version is
not included in this list, you have three options:
* Update the minimum required version of CMake. This may require
adaptions of other parts of our CMake scripts and has the most
impact on other MITK developers. Yet this is the safest and
cleanest option.
* Set Boost_ADDITIONAL_VERSIONS (see the documentation of the
FindBoost module). As Boost libraries and dependencies between
them are hard-coded in the FindBoost module only for known versions,
this may cause trouble for other MITK developers relying on new
components of Boost or components with changed dependencies.
* Copy a newer version of the FindBoost module into our CMake
directory. Our CMake directory has a higher precedence than the
default CMake module directory. Doublecheck if the minimum required
version of CMake is able to process the newer version of the
FindBoost module. Also, DO NOT FORGET to mention this option right
above the call of cmake_minimum_required() in the top-level
CMakeLists.txt file AND in this file right above the set(url)
command below so if we update the minimum required version of CMake
or use another option in the future, we do not forget to remove our
copy of the FindBoost module again. ]]
set(url "${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/boost_1_78_0_b1.tar.gz")
set(md5 bbaa634603e3789d7dd0c21d0bdf4f09)
if(MITK_USE_Boost_LIBRARIES)
#[[ Boost has a two-step build process. In the first step, a bootstrap
script is called to build b2, an executable that is used to actually
build Boost in the second step.
The bootstrap script expects a toolset (compiler) argument that is
used to build the b2 executable. The scripts and their expected
argument format differ between Windows and Unix. ]]
if(WIN32)
mitkFunctionGetMSVCVersion()
if(VISUAL_STUDIO_VERSION_MINOR EQUAL 0)
#[[ Use just the major version in the toolset name. ]]
set(bootstrap_args vc${VISUAL_STUDIO_VERSION_MAJOR})
elseif(VISUAL_STUDIO_VERSION_MAJOR EQUAL 14 AND VISUAL_STUDIO_VERSION_MINOR LESS 20)
#[[ Assume Visual Studio 2017. ]]
set(bootstrap_args vc${VISUAL_STUDIO_VERSION_MAJOR}1)
elseif(VISUAL_STUDIO_VERSION_MAJOR EQUAL 14 AND VISUAL_STUDIO_VERSION_MINOR LESS 30)
#[[ Assume Visual Studio 2019. ]]
set(bootstrap_args vc${VISUAL_STUDIO_VERSION_MAJOR}2)
elseif(VISUAL_STUDIO_VERSION_MAJOR EQUAL 14 AND VISUAL_STUDIO_VERSION_MINOR LESS 40)
#[[ Assume Visual Studio 2022. ]]
set(bootstrap_args vc${VISUAL_STUDIO_VERSION_MAJOR}3)
else()
#[[ Fallback to the generic case. Be prepared to add another elseif
branch above for future versions of Visual Studio. ]]
set(bootstrap_args vc${VISUAL_STUDIO_VERSION_MAJOR})
endif()
else()
#[[ We support GCC and Clang on Unix. On macOS, the toolset must be set
to clang. The actual compiler for all of these toolkits is set
further below, after the bootstrap script but before b2. ]]
if(CMAKE_CXX_COMPILER_ID STREQUAL GNU)
set(toolset gcc)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL Clang OR APPLE)
set(toolset clang)
endif()
if(toolset)
set(bootstrap_args --with-toolset=${toolset})
endif()
#[[ At least give it a shot if the toolset is something else and let
the bootstrap script decide on the toolset by not passing any
argument. ]]
endif()
#[[ The call of b2 is more complex. b2 arguments are grouped into options
and properties. Options follow the standard format for arguments while
properties are plain key-value pairs. ]]
set(b2_options
--build-dir=<BINARY_DIR>
--stagedir=<INSTALL_DIR>
--ignore-site-config #[[ Build independent of any site.config file ]]
-q #[[ Stop at first error ]]
)
if(APPLE AND CMAKE_OSX_SYSROOT)
#[[ Specify the macOS platform SDK to be used. ]]
list(APPEND b2_options --sysroot=${CMAKE_OSX_SYSROOT})
endif()
foreach(lib ${MITK_USE_Boost_LIBRARIES})
list(APPEND b2_options --with-${lib})
endforeach()
set(b2_properties
threading=multi
runtime-link=shared
- "cxxflags=${MITK_CXX14_FLAG} ${CMAKE_CXX_FLAGS}"
+ "cxxflags=${MITK_CXX${MITK_CXX_STANDARD}_FLAG} ${CMAKE_CXX_FLAGS}"
)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
list(APPEND b2_properties address-model=64)
else()
list(APPEND b2_properties address-model=32)
endif()
if(BUILD_SHARED_LIBS)
list(APPEND b2_properties link=shared)
else()
list(APPEND b2_properties link=static)
endif()
list(APPEND b2_properties "\
$<$<CONFIG:Debug>:variant=debug>\
$<$<CONFIG:Release>:variant=release>\
$<$<CONFIG:MinSizeRel>:variant=release>\
$<$<CONFIG:RelWithDebInfo>:variant=release>")
if(WIN32)
set(bootstrap_cmd if not exist b2.exe \( call bootstrap.bat ${bootstrap_args} \))
set(b2_cmd b2 ${b2_options} ${b2_properties} stage)
else()
set(bootstrap_cmd #[[ test -e ./b2 || ]] ./bootstrap.sh ${bootstrap_args})
set(b2_cmd ./b2 ${b2_options} ${b2_properties} stage)
#[[ We already told Boost if we want to use GCC or Clang but so far we
were not able to specify the exact same compiler we set in CMake
when configuring the MITK superbuild for the first time.
For example, this can be different from the system default
when multiple versions of the same compiler are installed
at the same time.
The bootstrap script creates a configuration file for b2 that should
be modified if necessary before b2 is called.
We look for a line like
using gcc ;
and replace it with something more specific like
using gcc : : /usr/bin/gcc-7.3 ;
We use the stream editor sed for the replacement but since macOS is
based on BSD Unix, we use the limited but portable BSD syntax
instead of the more powerful GNU syntax. We also use | instead of
the more commonly used / separator for sed because the replacement
contains slashes.
2021/06/15: The custom project-config.jam does not work well with
SDK paths on macOS anymore, so we use a custom project-config.jam
only on Linux for now. ]]
if(toolset AND NOT APPLE)
set(configure_cmd sed -i.backup "\
s|\
using[[:space:]][[:space:]]*${toolset}[[:space:]]*$<SEMICOLON>|\
using ${toolset} : : ${CMAKE_CXX_COMPILER} $<SEMICOLON>|\
g"
<SOURCE_DIR>/project-config.jam
)
endif()
endif()
endif()
if(WIN32)
set(dummy_cmd cd .)
else()
set(dummy_cmd true) #[[ "cd ." does not work reliably ]]
endif()
if(NOT patch_cmd)
set(patch_cmd ${dummy_cmd}) #[[ Do nothing ]]
endif()
if(NOT configure_cmd)
set(configure_cmd ${dummy_cmd}) #[[ Do nothing ]]
endif()
if(WIN32)
set(install_cmd
if not exist $<SHELL_PATH:${ep_prefix}/include/boost/config.hpp>
\( ${CMAKE_COMMAND} -E copy_directory <SOURCE_DIR>/boost <INSTALL_DIR>/include/boost \)
)
else()
set(install_cmd
# test -e <INSTALL_DIR>/include/boost/config.hpp ||
${CMAKE_COMMAND} -E copy_directory <SOURCE_DIR>/boost <INSTALL_DIR>/include/boost
)
endif()
ExternalProject_Add(${proj}
URL ${url}
URL_MD5 ${md5}
PATCH_COMMAND ${patch_cmd}
CONFIGURE_COMMAND ${configure_cmd}
BUILD_COMMAND ""
INSTALL_COMMAND ${install_cmd}
)
ExternalProject_Add_Step(${proj} bootstrap
COMMAND ${bootstrap_cmd}
DEPENDEES patch
DEPENDERS configure
WORKING_DIRECTORY <SOURCE_DIR>
)
ExternalProject_Add_Step(${proj} b2
COMMAND ${b2_cmd}
DEPENDEES bootstrap
DEPENDERS build
WORKING_DIRECTORY <SOURCE_DIR>
)
if(WIN32)
#[[ Reuse already extracted files. ]]
set(stamp_dir ${ep_prefix}/src/Boost-stamp)
configure_file(
${CMAKE_CURRENT_LIST_DIR}/extract-Boost.replacement.cmake
${stamp_dir}/extract-Boost.replacement.cmake
COPYONLY)
ExternalProject_Add_Step(${proj} pre_download
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/Boost-pre_download.cmake
DEPENDEES mkdir
DEPENDERS download
WORKING_DIRECTORY ${stamp_dir}
)
endif()
set(install_manifest_dependees install)
if(MITK_USE_Boost_LIBRARIES)
if(WIN32)
#[[ Move DLLs from lib to bin directory. ]]
ExternalProject_Add_Step(${proj} post_install
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/Boost-post_install-WIN32.cmake
DEPENDEES install
WORKING_DIRECTORY <INSTALL_DIR>/lib
)
set(install_manifest_dependees post_install)
elseif(APPLE)
#[[ Boost does not follow the common practice of either using rpath or
absolute paths for referencing dependencies. We have to use the
install_name_tool to fix this. ]]
ExternalProject_Add_Step(${proj} post_install
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/Boost-post_install-APPLE.cmake
DEPENDEES install
WORKING_DIRECTORY <INSTALL_DIR>/lib
)
set(install_manifest_dependees post_install)
endif()
endif()
ExternalProject_Add_Step(${proj} install_manifest
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/Boost-install_manifest.cmake
DEPENDEES ${install_manifest_dependees}
WORKING_DIRECTORY ${ep_prefix}
)
else()
mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}")
endif()
diff --git a/CMakeExternals/DCMQI.cmake b/CMakeExternals/DCMQI.cmake
index 933a8a4f69..3ab55c42e1 100644
--- a/CMakeExternals/DCMQI.cmake
+++ b/CMakeExternals/DCMQI.cmake
@@ -1,65 +1,69 @@
#-----------------------------------------------------------------------------
# DCMQI
#-----------------------------------------------------------------------------
if(MITK_USE_DCMQI)
# Sanity checks
if(DEFINED DCMQI_DIR AND NOT EXISTS ${DCMQI_DIR})
message(FATAL_ERROR "DCMQI_DIR variable is defined but corresponds to non-existing directory")
endif()
set(proj DCMQI)
set(proj_DEPENDENCIES DCMTK ITK)
set(DCMQI_DEPENDS ${proj})
if(NOT DEFINED DCMQI_DIR)
set(additional_cmake_args)
if(CTEST_USE_LAUNCHERS)
list(APPEND additional_cmake_args
"-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake"
)
endif()
+ if(MITK_USE_OpenCV)
+ list(APPEND additional_cmake_args "-DCMAKE_CONFIGURATION_TYPES:STRING=Debug$<SEMICOLON>Release")
+ endif()
+
mitk_query_custom_ep_vars()
ExternalProject_Add(${proj}
LIST_SEPARATOR ${sep}
GIT_REPOSITORY https://github.com/QIICR/dcmqi.git
GIT_TAG v1.2.4
UPDATE_COMMAND ""
INSTALL_COMMAND ""
CMAKE_GENERATOR ${gen}
CMAKE_GENERATOR_PLATFORM ${gen_platform}
CMAKE_ARGS
${ep_common_args}
${additional_cmake_args}
#-DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
-DBUILD_SHARED_LIBS:BOOL=ON
-DDCMQI_BUILD_APPS:BOOL=OFF
-DDCMTK_DIR:PATH=${DCMTK_DIR}
-DITK_DIR:PATH=${ITK_DIR}
-DITK_NO_IO_FACTORY_REGISTER_MANAGER:BOOL=ON
-DDCMQI_SUPERBUILD:BOOL=OFF
- -DDCMQI_CMAKE_CXX_STANDARD:STRING=14
+ -DDCMQI_CMAKE_CXX_STANDARD:STRING=${MITK_CXX_STANDARD}
${${proj}_CUSTOM_CMAKE_ARGS}
CMAKE_CACHE_ARGS
${ep_common_cache_args}
${${proj}_CUSTOM_CMAKE_CACHE_ARGS}
CMAKE_CACHE_DEFAULT_ARGS
${ep_common_cache_default_args}
${${proj}_CUSTOM_CMAKE_CACHE_DEFAULT_ARGS}
DEPENDS ${proj_DEPENDENCIES}
)
ExternalProject_Get_Property(${proj} binary_dir)
set(DCMQI_DIR ${binary_dir})
#set(${proj}_DIR ${ep_prefix})
#message(${proj}_DIR: ${${proj}_DIR})
#mitkFunctionInstallExternalCMakeProject(${proj})
else()
mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}")
endif()
endif()
diff --git a/CMakeExternals/DCMTK.cmake b/CMakeExternals/DCMTK.cmake
index 2f00eea2ef..5c2cec181a 100644
--- a/CMakeExternals/DCMTK.cmake
+++ b/CMakeExternals/DCMTK.cmake
@@ -1,70 +1,71 @@
#-----------------------------------------------------------------------------
# DCMTK
#-----------------------------------------------------------------------------
if(MITK_USE_DCMTK)
# Sanity checks
if(DEFINED DCMTK_DIR AND NOT EXISTS ${DCMTK_DIR})
message(FATAL_ERROR "DCMTK_DIR variable is defined but corresponds to non-existing directory")
endif()
set(proj DCMTK)
set(proj_DEPENDENCIES )
set(DCMTK_DEPENDS ${proj})
if(NOT DEFINED DCMTK_DIR)
if(DCMTK_DICOM_ROOT_ID)
set(DCMTK_CXX_FLAGS "${DCMTK_CXX_FLAGS} -DSITE_UID_ROOT=\\\"${DCMTK_DICOM_ROOT_ID}\\\"")
set(DCMTK_C_FLAGS "${DCMTK_CXX_FLAGS} -DSITE_UID_ROOT=\\\"${DCMTK_DICOM_ROOT_ID}\\\"")
endif()
set(additional_args )
if(CTEST_USE_LAUNCHERS)
list(APPEND additional_args
"-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake"
)
endif()
mitk_query_custom_ep_vars()
ExternalProject_Add(${proj}
LIST_SEPARATOR ${sep}
URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/dcmtk-3.6.6.tar.gz
URL_MD5 f815879d315b916366a9da71339c7575
CMAKE_GENERATOR ${gen}
CMAKE_GENERATOR_PLATFORM ${gen_platform}
CMAKE_ARGS
${ep_common_args}
${additional_args}
"-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS} ${DCMTK_CXX_FLAGS}"
"-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS} ${DCMTK_C_FLAGS}"
-DDCMTK_ENABLE_BUILTIN_DICTIONARY:BOOL=ON
-DDCMTK_ENABLE_CXX11:BOOL=ON
+ -DDCMTK_CXX11_FLAGS:STRING=-std=c++${MITK_CXX_STANDARD}
-DDCMTK_ENABLE_STL:BOOL=ON
-DDCMTK_WITH_DOXYGEN:BOOL=OFF
-DDCMTK_WITH_ZLIB:BOOL=OFF # see bug #9894
-DDCMTK_WITH_OPENSSL:BOOL=OFF # see bug #9894
-DDCMTK_WITH_PNG:BOOL=OFF # see bug #9894
-DDCMTK_WITH_TIFF:BOOL=OFF # see bug #9894
-DDCMTK_WITH_XML:BOOL=OFF # see bug #9894
-DDCMTK_WITH_ICONV:BOOL=OFF # see bug #9894
-DDCMTK_WITH_ICU:BOOL=OFF # see T26438
${${proj}_CUSTOM_CMAKE_ARGS}
CMAKE_CACHE_ARGS
${ep_common_cache_args}
${${proj}_CUSTOM_CMAKE_CACHE_ARGS}
CMAKE_CACHE_DEFAULT_ARGS
${ep_common_cache_default_args}
${${proj}_CUSTOM_CMAKE_CACHE_DEFAULT_ARGS}
DEPENDS ${proj_DEPENDENCIES}
)
set(DCMTK_DIR ${ep_prefix})
mitkFunctionInstallExternalCMakeProject(${proj})
else()
mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}")
endif()
endif()
diff --git a/CMakeExternals/Eigen.cmake b/CMakeExternals/Eigen.cmake
index effd8026f9..d54af9fdd2 100644
--- a/CMakeExternals/Eigen.cmake
+++ b/CMakeExternals/Eigen.cmake
@@ -1,44 +1,43 @@
#-----------------------------------------------------------------------------
# Eigen
#-----------------------------------------------------------------------------
if(MITK_USE_Eigen)
# Sanity checks
if(DEFINED Eigen_DIR AND NOT EXISTS ${Eigen_DIR})
message(FATAL_ERROR "Eigen_DIR variable is defined but corresponds to non-existing directory")
endif()
set(proj Eigen)
set(proj_DEPENDENCIES )
set(Eigen_DEPENDS ${proj})
if(NOT DEFINED Eigen_DIR)
ExternalProject_Add(${proj}
LIST_SEPARATOR ${sep}
- URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/eigen-eigen-07105f7124f9.tar.bz2
- URL_MD5 9e3bfaaab3db18253cfd87ea697b3ab1
- PATCH_COMMAND ${PATCH_COMMAND} -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/Eigen.patch
+ URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/eigen-3.4.0.tar.gz
+ URL_MD5 4c527a9171d71a72a9d4186e65bea559
CMAKE_GENERATOR ${gen}
CMAKE_GENERATOR_PLATFORM ${gen_platform}
CMAKE_ARGS
${ep_common_args}
-DBUILD_TESTING:BOOL=ON
-DEIGEN_BUILD_PKGCONFIG:BOOL=OFF
CMAKE_CACHE_ARGS
${ep_common_cache_args}
CMAKE_CACHE_DEFAULT_ARGS
${ep_common_cache_default_args}
)
set(Eigen_DIR ${ep_prefix})
mitkFunctionInstallExternalCMakeProject(${proj})
else()
mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}")
endif()
endif()
diff --git a/CMakeExternals/Eigen.patch b/CMakeExternals/Eigen.patch
deleted file mode 100644
index 688fde68e2..0000000000
--- a/CMakeExternals/Eigen.patch
+++ /dev/null
@@ -1,16 +0,0 @@
---- a/cmake/EigenConfigureTesting.cmake 2016-02-16 08:26:15.000000000 -0500
-+++ b/cmake/EigenConfigureTesting.cmake 2016-06-09 16:49:26.074261584 -0400
-@@ -14,13 +14,6 @@
- # check whether /bin/bash exists
- find_file(EIGEN_BIN_BASH_EXISTS "/bin/bash" PATHS "/" NO_DEFAULT_PATH)
-
--# CMake/Ctest does not allow us to change the build command,
--# so we have to workaround by directly editing the generated DartConfiguration.tcl file
--# save CMAKE_MAKE_PROGRAM
--set(CMAKE_MAKE_PROGRAM_SAVE ${CMAKE_MAKE_PROGRAM})
--# and set a fake one
--set(CMAKE_MAKE_PROGRAM "@EIGEN_MAKECOMMAND_PLACEHOLDER@")
--
- # This call activates testing and generates the DartConfiguration.tcl
- include(CTest)
-
diff --git a/CMakeExternals/GDCM.cmake b/CMakeExternals/GDCM.cmake
index 0d384ba1e8..5d09b10c61 100644
--- a/CMakeExternals/GDCM.cmake
+++ b/CMakeExternals/GDCM.cmake
@@ -1,71 +1,71 @@
#-----------------------------------------------------------------------------
# GDCM
#-----------------------------------------------------------------------------
# Sanity checks
if(DEFINED GDCM_DIR AND NOT EXISTS ${GDCM_DIR})
message(FATAL_ERROR "GDCM_DIR variable is defined but corresponds to non-existing directory")
endif()
# Check if an external ITK build tree was specified.
# If yes, use the GDCM from ITK, otherwise ITK will complain
if(ITK_DIR)
find_package(ITK)
if(ITK_GDCM_DIR)
set(GDCM_DIR ${ITK_GDCM_DIR})
endif()
endif()
set(proj GDCM)
set(proj_DEPENDENCIES )
set(GDCM_DEPENDS ${proj})
if(NOT DEFINED GDCM_DIR)
set(additional_args )
if(CTEST_USE_LAUNCHERS)
list(APPEND additional_args
"-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake"
)
endif()
# On Mac some assertions fail that prevent reading certain DICOM files. Bug #19995
if(APPLE)
list(APPEND additional_args
"-DCMAKE_CXX_FLAGS_DEBUG:STRING=${CMAKE_CXX_FLAGS_DEBUG} -DNDEBUG"
)
endif()
mitk_query_custom_ep_vars()
ExternalProject_Add(${proj}
LIST_SEPARATOR ${sep}
- URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/gdcm-3.0.8.tar.gz
- URL_MD5 29e0e60b04183e3eb9c18ad093156b2c
+ URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/gdcm-3.0.10.tar.gz
+ URL_MD5 28c70d02c2005a8c9d2a5847c8ba3c00
CMAKE_GENERATOR ${gen}
CMAKE_GENERATOR_PLATFORM ${gen_platform}
CMAKE_ARGS
${ep_common_args}
${additional_args}
-DGDCM_BUILD_SHARED_LIBS:BOOL=ON
-DGDCM_BUILD_DOCBOOK_MANPAGES:BOOL=OFF
${${proj}_CUSTOM_CMAKE_ARGS}
CMAKE_CACHE_ARGS
${ep_common_cache_args}
${${proj}_CUSTOM_CMAKE_CACHE_ARGS}
CMAKE_CACHE_DEFAULT_ARGS
${ep_common_cache_default_args}
${${proj}_CUSTOM_CMAKE_CACHE_DEFAULT_ARGS}
DEPENDS ${proj_DEPENDENCIES}
)
set(GDCM_DIR ${ep_prefix})
mitkFunctionInstallExternalCMakeProject(${proj})
else()
mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}")
find_package(GDCM)
endif()
diff --git a/CMakeExternals/GDCM.patch b/CMakeExternals/GDCM.patch
deleted file mode 100644
index 883cbcc29d..0000000000
--- a/CMakeExternals/GDCM.patch
+++ /dev/null
@@ -1,90 +0,0 @@
-commit 60bdeceff015258608f9d798268853ec4a76fc7d
-Author: Christoph Kolb <c.kolb@dkfz-heidelberg.de>
-Date: Wed May 18 12:13:37 2016 +0200
-
- Prevent building man pages
-
-diff --git a/Utilities/doxygen/CMakeLists.txt b/Utilities/doxygen/CMakeLists.txt
-index cf8f616..65608fd 100644
---- a/Utilities/doxygen/CMakeLists.txt
-+++ b/Utilities/doxygen/CMakeLists.txt
-@@ -224,79 +224,3 @@ ${CMAKE_CURRENT_BINARY_DIR}/latex/Makefile
- subdirs(vtk)
- endif()
- endif()
--
--set(MANPAGES_XML
-- gdcm2pnm
-- gdcm2vtk
-- gdcmanon
-- gdcmconv
-- gdcmdiff
-- gdcmdump
-- gdcmgendir
-- gdcmimg
-- gdcminfo
-- gdcmpap3
-- gdcmpdf
-- gdcmraw
-- gdcmscanner
-- gdcmscu
-- gdcmtar
-- gdcmviewer
-- gdcmxml
-- )
--find_package(LibXslt)
--# need an XSLT 1.0 processor, use xsltproc, maybe add more implementations later
--if(LIBXSLT_XSLTPROC_EXECUTABLE)
-- set(XSLT_PROCESSOR ${LIBXSLT_XSLTPROC_EXECUTABLE})
--# http://docbook.sourceforge.net/release/xsl/current/doc/manpages/man.output.quietly.html
-- set(XSLT_PROCESSOR_ARG --param man.output.quietly 1)
-- # User can change the behavior at cmake time:
-- if(NOT DEFINED GDCM_MANPAGES_USE_NONET)
-- set(GDCM_MANPAGES_USE_NONET FALSE)
-- # By default I want nonet on Debian:
-- if(EXISTS /etc/xml/catalog)
-- set(GDCM_MANPAGES_USE_NONET TRUE)
-- endif()
-- endif()
-- if(GDCM_MANPAGES_USE_NONET)
-- list(APPEND XSLT_PROCESSOR_ARG --nonet)
-- endif()
--else()
-- find_program(MSXSL_EXECUTABLE
-- msxsl
-- )
-- if(MSXSL_EXECUTABLE)
-- set(XSLT_PROCESSOR ${MSXSL_EXECUTABLE})
-- set(XSLT_PROCESSOR_ARG "-pi")
--# msxsl.exe -pi gdcmimg.xml
--#
--#Error occurred while executing stylesheet 'http://docbook.sourceforge.net/release/xsl-ns/current/manpages/docbook.xsl'.
--#
--#Code: 0x80004005
--#Namespace 'http://exslt.org/common' does not contain any functions.
--# -> http://stackoverflow.com/a/16605034/136285 ??
-- endif()
--endif()
--if(XSLT_PROCESSOR)
--foreach(docbook ${MANPAGES_XML})
-- add_custom_command(
-- OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${docbook}.1
-- COMMAND ${XSLT_PROCESSOR} ${XSLT_PROCESSOR_ARG} ${CMAKE_CURRENT_SOURCE_DIR}/man/${docbook}.xml
-- DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/man/${docbook}.xml
-- )
-- list(APPEND MANPAGES
-- ${CMAKE_CURRENT_BINARY_DIR}/${docbook}.1
-- )
--endforeach()
--add_custom_target(DOCBOOK_MANPAGES
-- ALL
-- DEPENDS ${MANPAGES}
-- COMMENT "docbook manpages"
-- )
--install(FILES
-- ${MANPAGES}
-- DESTINATION ${GDCM_INSTALL_MAN_DIR}/man1 COMPONENT DebugDevel
--)
--else()
-- message(WARNING "Cannot build man page from docbook (need an XSL processor)")
--endif()
diff --git a/CMakeExternals/ITK-4.13.3.patch b/CMakeExternals/ITK-4.13.3.patch
deleted file mode 100644
index a21a50b384..0000000000
--- a/CMakeExternals/ITK-4.13.3.patch
+++ /dev/null
@@ -1,53 +0,0 @@
-diff --git a/Modules/Core/Common/include/itkBoundingBox.h b/Modules/Core/Common/include/itkBoundingBox.h
-index 36b03aaa7f..64205acfab 100644
---- a/Modules/Core/Common/include/itkBoundingBox.h
-+++ b/Modules/Core/Common/include/itkBoundingBox.h
-@@ -87,7 +87,7 @@ public:
- itkTypeMacro(BoundingBox, Object);
-
- /** Method for creation through the object factory. */
-- itkNewMacro(Self);
-+ itkFactorylessNewMacro(Self);
-
- /** Hold on to the type information specified by the template parameters. */
- typedef TPointIdentifier PointIdentifier;
-diff --git a/Modules/Core/Common/include/itkVectorContainer.h b/Modules/Core/Common/include/itkVectorContainer.h
-index 72293c4ce8..e6366c66b5 100644
---- a/Modules/Core/Common/include/itkVectorContainer.h
-+++ b/Modules/Core/Common/include/itkVectorContainer.h
-@@ -92,7 +92,7 @@ public:
- typedef VectorType STLContainerType;
-
- /** Method for creation through the object factory. */
-- itkNewMacro(Self);
-+ itkFactorylessNewMacro(Self);
-
- /** Standard part of every itk Object. */
- itkTypeMacro(VectorContainer, Object);
-diff --git a/Modules/Core/Transform/include/itkScalableAffineTransform.h b/Modules/Core/Transform/include/itkScalableAffineTransform.h
-index 4e830476a2..b5efeb95a4 100644
---- a/Modules/Core/Transform/include/itkScalableAffineTransform.h
-+++ b/Modules/Core/Transform/include/itkScalableAffineTransform.h
-@@ -47,7 +47,8 @@ public:
- itkTypeMacro(ScalableAffineTransform, AffineTransform);
-
- /** New macro for creation of through a Smart Pointer */
-- itkNewMacro(Self);
-+ itkFactorylessNewMacro(Self);
-+ itkCloneMacro(Self);
-
- /** Dimension of the domain space. */
- itkStaticConstMacro(InputSpaceDimension, unsigned int, NDimensions);
-diff --git a/Modules/ThirdParty/OpenJPEG/src/openjpeg/opj_includes.h b/Modules/ThirdParty/OpenJPEG/src/openjpeg/opj_includes.h
-index e75a220d4d..90d5bf1218 100644
---- a/Modules/ThirdParty/OpenJPEG/src/openjpeg/opj_includes.h
-+++ b/Modules/ThirdParty/OpenJPEG/src/openjpeg/opj_includes.h
-@@ -88,7 +88,7 @@ Most compilers implement their own version of this keyword ...
- #endif
-
- /* MSVC and Borland C do not have lrintf */
--#if defined(_MSC_VER) || defined(__BORLANDC__)
-+#if (defined(_MSC_VER) && _MSC_VER < 1928) || defined(__BORLANDC__)
-
- /* MSVC 64bits doesn't support _asm */
- #if !defined(_WIN64)
diff --git a/CMakeExternals/ITK-5.2.1.patch b/CMakeExternals/ITK-5.2.1.patch
new file mode 100644
index 0000000000..8ad1cb5184
--- /dev/null
+++ b/CMakeExternals/ITK-5.2.1.patch
@@ -0,0 +1,35 @@
+diff --git a/Modules/Video/BridgeOpenCV/include/itkOpenCVVideoIO.h b/Modules/Video/BridgeOpenCV/include/itkOpenCVVideoIO.h
+index 1570a182..96a70f17 100644
+--- a/Modules/Video/BridgeOpenCV/include/itkOpenCVVideoIO.h
++++ b/Modules/Video/BridgeOpenCV/include/itkOpenCVVideoIO.h
+@@ -168,7 +168,7 @@ public:
+ const std::vector<SizeValueType> & dim,
+ const char * fourCC,
+ unsigned int nChannels,
+- IOComponentType componentType);
++ IOComponentEnum componentType);
+
+ protected:
+ OpenCVVideoIO();
+diff --git a/Modules/Video/BridgeOpenCV/src/itkOpenCVVideoIO.cxx b/Modules/Video/BridgeOpenCV/src/itkOpenCVVideoIO.cxx
+index 7d7db3b8..8ce71c8f 100644
+--- a/Modules/Video/BridgeOpenCV/src/itkOpenCVVideoIO.cxx
++++ b/Modules/Video/BridgeOpenCV/src/itkOpenCVVideoIO.cxx
+@@ -436,7 +436,7 @@ OpenCVVideoIO::SetWriterParameters(TemporalRatioType fps,
+ const std::vector<SizeValueType> & dim,
+ const char * fourCC,
+ unsigned int nChannels,
+- IOComponentType componentType)
++ IOComponentEnum componentType)
+ {
+ if (this->m_ReaderOpen || this->m_WriterOpen)
+ {
+@@ -444,7 +444,7 @@ OpenCVVideoIO::SetWriterParameters(TemporalRatioType fps,
+ }
+
+ // Make sure componentType is acceptable (right now we only support char)
+- if (componentType != UCHAR)
++ if (componentType != IOComponentEnum::UCHAR)
+ {
+ itkExceptionMacro("OpenCV IO only supports writing video with pixels of UCHAR");
+ }
diff --git a/CMakeExternals/ITK.cmake b/CMakeExternals/ITK.cmake
index 3869dd614a..b93d27df57 100644
--- a/CMakeExternals/ITK.cmake
+++ b/CMakeExternals/ITK.cmake
@@ -1,87 +1,89 @@
#-----------------------------------------------------------------------------
# ITK
#-----------------------------------------------------------------------------
# Sanity checks
if(DEFINED ITK_DIR AND NOT EXISTS ${ITK_DIR})
message(FATAL_ERROR "ITK_DIR variable is defined but corresponds to non-existing directory")
endif()
set(proj ITK)
set(proj_DEPENDENCIES GDCM)
if(MITK_USE_OpenCV)
list(APPEND proj_DEPENDENCIES OpenCV)
endif()
if(MITK_USE_HDF5)
list(APPEND proj_DEPENDENCIES HDF5)
endif()
set(ITK_DEPENDS ${proj})
if(NOT DEFINED ITK_DIR)
set(additional_cmake_args -DUSE_WRAP_ITK:BOOL=OFF)
+ list(APPEND additional_cmake_args
+ -DITKV4_COMPATIBILITY:BOOL=OFF
+ -DITK_LEGACY_REMOVE:BOOL=ON
+ )
+
if(MITK_USE_OpenCV)
list(APPEND additional_cmake_args
-DModule_ITKVideoBridgeOpenCV:BOOL=ON
-DOpenCV_DIR:PATH=${OpenCV_DIR}
+ "-DCMAKE_CONFIGURATION_TYPES:STRING=Debug$<SEMICOLON>Release"
)
endif()
# Keep the behaviour of ITK 4.3 which by default turned on ITK Review
# see MITK bug #17338
list(APPEND additional_cmake_args
-DModule_ITKReview:BOOL=ON
-DModule_ITKOpenJPEG:BOOL=ON # for 4.7, the OpenJPEG is needed by review but the variable must be set
-DModule_IsotropicWavelets:BOOL=ON
)
if(CTEST_USE_LAUNCHERS)
list(APPEND additional_cmake_args
"-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake"
)
endif()
mitk_query_custom_ep_vars()
ExternalProject_Add(${proj}
LIST_SEPARATOR ${sep}
UPDATE_COMMAND ""
- URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/InsightToolkit-4.13.3.tar.gz
- URL_MD5 d1c10c8288b47577d718a71190444815
- PATCH_COMMAND
- # 2021/03/26: Only the patch file changed since the last snapshot.
- # The only purpose of this comment is to change this .cmake file
- # to make our build system aware of a change.
- ${PATCH_COMMAND} -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/ITK-4.13.3.patch
+ URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/InsightToolkit-5.2.1.tar.gz
+ URL_MD5 48c1fe49f75fdaa91b31bbf9dda01a42
+ PATCH_COMMAND ${PATCH_COMMAND} -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/ITK-5.2.1.patch
CMAKE_GENERATOR ${gen}
CMAKE_GENERATOR_PLATFORM ${gen_platform}
CMAKE_ARGS
${ep_common_args}
${additional_cmake_args}
-DBUILD_EXAMPLES:BOOL=OFF
-DITK_USE_SYSTEM_GDCM:BOOL=ON
-DGDCM_DIR:PATH=${GDCM_DIR}
-DITK_USE_SYSTEM_HDF5:BOOL=ON
-DHDF5_DIR:PATH=${HDF5_DIR}
${${proj}_CUSTOM_CMAKE_ARGS}
CMAKE_CACHE_ARGS
${ep_common_cache_args}
${${proj}_CUSTOM_CMAKE_CACHE_ARGS}
CMAKE_CACHE_DEFAULT_ARGS
${ep_common_cache_default_args}
${${proj}_CUSTOM_CMAKE_CACHE_DEFAULT_ARGS}
DEPENDS ${proj_DEPENDENCIES}
)
set(ITK_DIR ${ep_prefix})
mitkFunctionInstallExternalCMakeProject(${proj})
else()
mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}")
endif()
diff --git a/CMakeExternals/MatchPoint.cmake b/CMakeExternals/MatchPoint.cmake
index ceba801875..85d8a0556f 100644
--- a/CMakeExternals/MatchPoint.cmake
+++ b/CMakeExternals/MatchPoint.cmake
@@ -1,69 +1,75 @@
#-----------------------------------------------------------------------------
# MatchPoint
#-----------------------------------------------------------------------------
if(MITK_USE_MatchPoint)
set(MatchPoint_SOURCE_DIR "" CACHE PATH "Location of the MatchPoint source directory")
mark_as_advanced(MatchPoint_SOURCE_DIR)
# Sanity checks
if(DEFINED MatchPoint_DIR AND NOT EXISTS ${MatchPoint_DIR})
message(FATAL_ERROR "MatchPoint_DIR variable is defined but corresponds to non-existing directory")
endif()
if(NOT MatchPoint_DIR AND MatchPoint_SOURCE_DIR AND NOT EXISTS ${MatchPoint_SOURCE_DIR})
message(FATAL_ERROR "MatchPoint_SOURCE_DIR variable is defined but corresponds to non-existing directory")
endif()
set(proj MatchPoint)
set(proj_DEPENDENCIES ITK)
set(MatchPoint_DEPENDS ${proj})
if(NOT MatchPoint_DIR)
+ set(additional_cmake_args)
+
+ if(MITK_USE_OpenCV)
+ list(APPEND additional_cmake_args "-DCMAKE_CONFIGURATION_TYPES:STRING=Debug$<SEMICOLON>Release")
+ endif()
+
if(MatchPoint_SOURCE_DIR)
set(download_step SOURCE_DIR ${MatchPoint_SOURCE_DIR})
else()
set(download_step
- URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/MatchPoint_rev_f2a64255.tar.gz
- URL_MD5 4407836f85e6382e65c9d3a4bd79c370
+ URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/MatchPoint_rev_f7699d1e.tar.gz
+ URL_MD5 8a24fbdccd6f18f158f49a0ad4fa3faa
)
endif()
string(REPLACE "-DBOOST_ALL_DYN_LINK" "" modified_ep_common_args "${ep_common_args}")
ExternalProject_Add(${proj}
${download_step}
# INSTALL_COMMAND "${CMAKE_COMMAND} -P cmake_install.cmake"
CMAKE_GENERATOR ${gen}
CMAKE_GENERATOR_PLATFORM ${gen_platform}
CMAKE_ARGS
${modified_ep_common_args}
${additional_cmake_args}
-DBUILD_TESTING:BOOL=OFF
-DITK_DIR:PATH=${ITK_DIR} #/src/ITK-build
-DMAP_USE_SYSTEM_GDCM:BOOL=ON
-DMAP_USE_SYSTEM_HDF5:BOOL=ON
-DMAP_DISABLE_ITK_IO_FACTORY_AUTO_REGISTER:BOOL=ON
-DMAP_WRAP_Plastimatch:BOOL=ON
-DMAP_BUILD_Ontology:BOOL=ON
-DMAP_BUILD_Ontology_simple:BOOL=ON
-DGDCM_DIR:PATH=${GDCM_DIR}
-DHDF5_DIR:PATH=${HDF5_DIR}
CMAKE_CACHE_ARGS
${ep_common_cache_args}
CMAKE_CACHE_DEFAULT_ARGS
${ep_common_cache_default_args}
DEPENDS ${proj_DEPENDENCIES}
)
ExternalProject_Get_Property(${proj} binary_dir)
set(${proj}_DIR ${binary_dir})
mitkFunctionInstallExternalCMakeProject(${proj})
else()
mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}")
endif()
endif(MITK_USE_MatchPoint)
diff --git a/CMakeExternals/OpenCV.cmake b/CMakeExternals/OpenCV.cmake
index 2a1e7ceb87..36aacee775 100644
--- a/CMakeExternals/OpenCV.cmake
+++ b/CMakeExternals/OpenCV.cmake
@@ -1,85 +1,85 @@
#-----------------------------------------------------------------------------
# OpenCV
#-----------------------------------------------------------------------------
if(MITK_USE_OpenCV)
# Sanity checks
if(DEFINED OpenCV_DIR AND NOT EXISTS ${OpenCV_DIR})
message(FATAL_ERROR "OpenCV_DIR variable is defined but corresponds to non-existing directory")
endif()
set(proj OpenCV)
set(proj_DEPENDENCIES)
set(OpenCV_DEPENDS ${proj})
if(NOT DEFINED OpenCV_DIR)
set(additional_cmake_args
-DBUILD_opencv_java:BOOL=OFF
-DBUILD_opencv_ts:BOOL=OFF
-DBUILD_PERF_TESTS:BOOL=OFF
-DBUILD_opencv_python:BOOL=OFF
-DBUILD_opencv_python3:BOOL=OFF
-DBUILD_opencv_python_bindings_generator:BOOL=OFF
#-DBUILD_NEW_PYTHON_SUPPORT:BOOL=OFF
)
if(MITK_USE_Qt5)
list(APPEND additional_cmake_args
-DWITH_QT:BOOL=OFF
-DWITH_QT_OPENGL:BOOL=OFF
-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}
)
endif()
if(CTEST_USE_LAUNCHERS)
list(APPEND additional_cmake_args
"-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake"
)
endif()
mitk_query_custom_ep_vars()
- set(opencv_url ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/opencv-3.4.8.tar.gz)
- set(opencv_url_md5 5aa8240c28c00a7dacdf51698e0ced77)
+ set(opencv_url ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/opencv-3.4.16.tar.gz)
+ set(opencv_url_md5 ce69441a75d3358e22fbfb579fab52a9)
ExternalProject_Add(${proj}
LIST_SEPARATOR ${sep}
URL ${opencv_url}
URL_MD5 ${opencv_url_md5}
CMAKE_GENERATOR ${gen}
CMAKE_GENERATOR_PLATFORM ${gen_platform}
CMAKE_ARGS
${ep_common_args}
-DBUILD_TESTS:BOOL=OFF
-DBUILD_DOCS:BOOL=OFF
-DBUILD_EXAMPLES:BOOL=OFF
-DBUILD_DOXYGEN_DOCS:BOOL=OFF
-DWITH_CUDA:BOOL=OFF
-DWITH_VTK:BOOL=OFF
-DENABLE_CXX11:BOOL=ON
-DWITH_IPP:BOOL=OFF
-DBUILD_IPP_IW:BOOL=OFF
-DENABLE_PRECOMPILED_HEADERS:BOOL=OFF
${additional_cmake_args}
${${proj}_CUSTOM_CMAKE_ARGS}
CMAKE_CACHE_ARGS
${ep_common_cache_args}
${${proj}_CUSTOM_CMAKE_CACHE_ARGS}
CMAKE_CACHE_DEFAULT_ARGS
${ep_common_cache_default_args}
${${proj}_CUSTOM_CMAKE_CACHE_DEFAULT_ARGS}
DEPENDS ${proj_DEPENDENCIES}
)
set(OpenCV_DIR ${ep_prefix})
mitkFunctionInstallExternalCMakeProject(${proj})
else()
mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}")
endif()
endif()
diff --git a/CMakeExternals/PCRE.cmake b/CMakeExternals/PCRE.cmake
index 9487afb1b1..c8a37b69c9 100644
--- a/CMakeExternals/PCRE.cmake
+++ b/CMakeExternals/PCRE.cmake
@@ -1,69 +1,69 @@
#--------------------------------------------------------------------------
# PCRE (Perl Compatible Regular Expressions)
#--------------------------------------------------------------------------
if(MITK_USE_PCRE)
if(DEFINED PCRE_DIR AND NOT EXISTS ${PCRE_DIR})
message(FATAL_ERROR "PCRE_DIR variable is defined but corresponds to non-existing directory")
endif()
set(proj PCRE)
set(${proj}_DEPENDENCIES "")
set(${proj}_DEPENDS ${proj})
if(NOT PCRE_DIR)
if(UNIX)
# Some other projects (e.g. Swig) require a pcre-config script which is not
# generated when using the CMake build system.
set(configure_cmd
CONFIGURE_COMMAND <SOURCE_DIR>/./configure
CC=${CMAKE_C_COMPILER}${CMAKE_C_COMPILER_ARG1}
CFLAGS=-fPIC
- "CXXFLAGS=-fPIC ${MITK_CXX14_FLAG} ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE}"
+ "CXXFLAGS=-fPIC ${MITK_CXX${MITK_CXX_STANDARD}_FLAG} ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE}"
"LDFLAGS=${CMAKE_LINKER_FLAGS} ${CMAKE_LINKER_FLAGS_RELEASE} ${_install_rpath_linkflag}"
CXX=${CMAKE_CXX_COMPILER}${CMAKE_CXX_COMPILER_ARG1}
--prefix=<INSTALL_DIR>
--disable-shared
--enable-jit
)
else()
set(additional_cmake_args )
if(CTEST_USE_LAUNCHERS)
list(APPEND additional_cmake_args
"-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake"
)
endif()
set(configure_cmd
CMAKE_GENERATOR ${gen}
CMAKE_GENERATOR_PLATFORM ${gen_platform}
CMAKE_ARGS
${ep_common_args}
${additional_cmake_args}
"-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS} -fPIC"
-DBUILD_SHARED_LIBS:BOOL=OFF
-DPCRE_BUILD_PCREGREP:BOOL=OFF
-DPCRE_BUILD_TESTS:BOOL=OFF
-DPCRE_SUPPORT_JIT:BOOL=ON
CMAKE_CACHE_ARGS
${ep_common_cache_args}
CMAKE_CACHE_DEFAULT_ARGS
${ep_common_cache_default_args}
)
endif()
ExternalProject_add(${proj}
LIST_SEPARATOR ${sep}
URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/pcre-8.35.tar.gz
URL_MD5 "ed58bcbe54d3b1d59e9f5415ef45ce1c"
${configure_cmd}
DEPENDS "${${proj}_DEPENDENCIES}"
)
set(PCRE_DIR ${ep_prefix})
else()
mitkMacroEmptyExternalProject(${proj} "${${proj}_DEPENDENCIES}")
endif()
endif()
diff --git a/CMakeExternals/VTK-9.0.1.patch b/CMakeExternals/VTK-9.0.1.patch
deleted file mode 100644
index 4bb1a4665c..0000000000
--- a/CMakeExternals/VTK-9.0.1.patch
+++ /dev/null
@@ -1,51 +0,0 @@
-diff --git a/Rendering/FreeType/vtkFreeTypeTools.cxx b/Rendering/FreeType/vtkFreeTypeTools.cxx
-index c54289dc..03b899c4 100644
---- a/Rendering/FreeType/vtkFreeTypeTools.cxx
-+++ b/Rendering/FreeType/vtkFreeTypeTools.cxx
-@@ -378,8 +378,7 @@ FTC_CMapCache* vtkFreeTypeTools::GetCMapCache()
- }
-
- //----------------------------------------------------------------------------
--FT_CALLBACK_DEF(FT_Error)
--vtkFreeTypeToolsFaceRequester(
-+static FT_Error vtkFreeTypeToolsFaceRequester(
- FTC_FaceID face_id, FT_Library lib, FT_Pointer request_data, FT_Face* face)
- {
- #if VTK_FTFC_DEBUG_CD
-diff --git a/ThirdParty/hdf5/vtkhdf5/src/H5Fsuper.c b/ThirdParty/hdf5/vtkhdf5/src/H5Fsuper.c
-index 49995651..00a3dc55 100644
---- a/ThirdParty/hdf5/vtkhdf5/src/H5Fsuper.c
-+++ b/ThirdParty/hdf5/vtkhdf5/src/H5Fsuper.c
-@@ -54,6 +54,7 @@
- /********************/
- static herr_t H5F__super_ext_create(H5F_t *f, H5O_loc_t *ext_ptr);
- static herr_t H5F__update_super_ext_driver_msg(H5F_t *f);
-+herr_t H5O__fsinfo_set_version(H5F_t *f, H5O_fsinfo_t *fsinfo);
-
-
- /*********************/
-diff --git a/ThirdParty/hdf5/vtkhdf5/src/H5Oint.c b/ThirdParty/hdf5/vtkhdf5/src/H5Oint.c
-index 543637c1..a927c0d1 100644
---- a/ThirdParty/hdf5/vtkhdf5/src/H5Oint.c
-+++ b/ThirdParty/hdf5/vtkhdf5/src/H5Oint.c
-@@ -82,6 +82,7 @@ static herr_t H5O__free_visit_visited(void *item, void *key,
- static herr_t H5O__visit_cb(hid_t group, const char *name, const H5L_info_t *linfo,
- void *_udata);
- static const H5O_obj_class_t *H5O__obj_class_real(const H5O_t *oh);
-+herr_t H5CX_get_ohdr_flags(uint8_t* oh_flags);
-
-
- /*********************/
-diff --git a/ThirdParty/hdf5/vtkhdf5/src/H5Rint.c b/ThirdParty/hdf5/vtkhdf5/src/H5Rint.c
-index 159bccac..08bd8c80 100644
---- a/ThirdParty/hdf5/vtkhdf5/src/H5Rint.c
-+++ b/ThirdParty/hdf5/vtkhdf5/src/H5Rint.c
-@@ -46,6 +46,8 @@
- /********************/
- /* Local Prototypes */
- /********************/
-+herr_t H5CX_set_libver_bounds(H5F_t*);
-+
-
- /*********************/
- /* Package Variables */
diff --git a/CMakeExternals/VTK.cmake b/CMakeExternals/VTK.cmake
index 767dff62a1..f0c58f66d5 100644
--- a/CMakeExternals/VTK.cmake
+++ b/CMakeExternals/VTK.cmake
@@ -1,88 +1,88 @@
#-----------------------------------------------------------------------------
# VTK
#-----------------------------------------------------------------------------
# Sanity checks
if(DEFINED VTK_DIR AND NOT EXISTS ${VTK_DIR})
message(FATAL_ERROR "VTK_DIR variable is defined but corresponds to non-existing directory")
endif()
set(proj VTK)
set(proj_DEPENDENCIES )
set(VTK_DEPENDS ${proj})
if(MITK_USE_HDF5)
list(APPEND proj_DEPENDENCIES HDF5)
endif()
if(NOT DEFINED VTK_DIR)
set(additional_cmake_args )
if(WIN32)
list(APPEND additional_cmake_args
-DCMAKE_CXX_MP_FLAG:BOOL=ON
)
else()
list(APPEND additional_cmake_args
-DVTK_MODULE_USE_EXTERNAL_VTK_freetype:BOOL=ON
)
endif()
# Optionally enable memory leak checks for any objects derived from vtkObject. This
# will force unit tests to fail if they have any of these memory leaks.
option(MITK_VTK_DEBUG_LEAKS OFF)
mark_as_advanced(MITK_VTK_DEBUG_LEAKS)
list(APPEND additional_cmake_args
-DVTK_DEBUG_LEAKS:BOOL=${MITK_VTK_DEBUG_LEAKS}
)
if(MITK_USE_Qt5)
list(APPEND additional_cmake_args
-DVTK_GROUP_ENABLE_Qt:STRING=YES
-DQt5_DIR:PATH=${Qt5_DIR}
)
endif()
if(CTEST_USE_LAUNCHERS)
list(APPEND additional_cmake_args
"-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake"
)
endif()
mitk_query_custom_ep_vars()
ExternalProject_Add(${proj}
LIST_SEPARATOR ${sep}
- URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/VTK-9.0.1.tar.gz
- URL_MD5 f443c9198495081765910ebbc9dace3a
- PATCH_COMMAND
- ${PATCH_COMMAND} -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/VTK-9.0.1.patch
+ URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/VTK-9.1.0.tar.gz
+ URL_MD5 96508e51d7c3764cd5aba06fffd9864e
CMAKE_GENERATOR ${gen}
CMAKE_GENERATOR_PLATFORM ${gen_platform}
CMAKE_ARGS
${ep_common_args}
+ -DOpenGL_GL_PREFERENCE:STRING=LEGACY
-DVTK_ENABLE_WRAPPING:BOOL=OFF
-DVTK_LEGACY_REMOVE:BOOL=ON
-DVTK_MODULE_ENABLE_VTK_TestingRendering:STRING=YES
-DVTK_MODULE_ENABLE_VTK_RenderingContextOpenGL2:STRING=YES
-DVTK_MODULE_ENABLE_VTK_RenderingVolumeOpenGL2:STRING=YES
+ -DVTK_MODULE_ENABLE_VTK_GUISupportQtQuick:STRING=NO
${additional_cmake_args}
${${proj}_CUSTOM_CMAKE_ARGS}
CMAKE_CACHE_ARGS
${ep_common_cache_args}
${${proj}_CUSTOM_CMAKE_CACHE_ARGS}
CMAKE_CACHE_DEFAULT_ARGS
${ep_common_cache_default_args}
${${proj}_CUSTOM_CMAKE_CACHE_DEFAULT_ARGS}
DEPENDS ${proj_DEPENDENCIES}
)
set(VTK_DIR ${ep_prefix})
mitkFunctionInstallExternalCMakeProject(${proj})
else()
mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}")
endif()
diff --git a/CMakeExternals/Vigra.cmake b/CMakeExternals/Vigra.cmake
index 80e0fd99ee..f79b5ff82c 100644
--- a/CMakeExternals/Vigra.cmake
+++ b/CMakeExternals/Vigra.cmake
@@ -1,65 +1,72 @@
#-----------------------------------------------------------------------------
# VIGRA
#-----------------------------------------------------------------------------
if(MITK_USE_Vigra)
# Sanity checks
if(DEFINED Vigra_DIR AND NOT EXISTS ${Vigra_DIR})
message(FATAL_ERROR "Vigra_DIR variable is defined but corresponds to non-existing directory")
endif()
if(NOT ${MITK_USE_HDF5})
message(FATAL_ERROR "HDF5 is required for Vigra. Please enable it.")
endif()
set(proj Vigra)
set(proj_DEPENDENCIES HDF5)
set(Vigra_DEPENDS ${proj})
# If a mac ports installation is present some imaging-io-libraries may interfere with the vigra build.
# Hence, we exclude them here.
set(mac_additional_cmake_args)
if(APPLE)
- set(mac_additional_cmake_args -DJPEG_INCLUDE_DIR= -DJPEG_LIBRARY= -DTIFF_INCLUDE_DIR= -DTIFF_LIBRARY= -DPNG_LIBRARY_RELEASE= -DPNG_PNG_INCLUDE_DIR= )
+ set(mac_additional_cmake_args
+ -DJPEG_INCLUDE_DIR=
+ -DJPEG_LIBRARY=
+ -DTIFF_INCLUDE_DIR=
+ -DTIFF_LIBRARY=
+ -DPNG_LIBRARY_RELEASE=
+ -DPNG_PNG_INCLUDE_DIR=
+ )
endif()
if(NOT DEFINED Vigra_DIR)
set(additional_cmake_args )
if(CTEST_USE_LAUNCHERS)
list(APPEND additional_cmake_args
"-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake"
)
endif()
ExternalProject_Add(${proj}
URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/vigra-1.10.0-src.tar.gz
URL_MD5 4f963f0be4fcb8b06271c2aa40baa9be
PATCH_COMMAND ${PATCH_COMMAND} -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/Vigra.patch
CMAKE_GENERATOR ${gen}
CMAKE_GENERATOR_PLATFORM ${gen_platform}
CMAKE_ARGS
${ep_common_args}
${additional_cmake_args}
${mac_additional_cmake_args}
-DAUTOEXEC_TESTS:BOOL=OFF
-DWITH_VIGRANUMPY:BOOL=OFF
-DHDF5_DIR:PATH=${HDF5_DIR}
-DCMAKE_INSTALL_RPATH_USE_LINK_PATH:BOOL=TRUE
-DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
CMAKE_CACHE_ARGS
${ep_common_cache_args}
CMAKE_CACHE_DEFAULT_ARGS
${ep_common_cache_default_args}
DEPENDS ${proj_DEPENDENCIES}
)
set(Vigra_DIR ${ep_prefix})
else()
mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}")
endif()
endif(MITK_USE_Vigra)
diff --git a/CMakeExternals/Vigra.patch b/CMakeExternals/Vigra.patch
index 3a2ed8e943..8002ed96bb 100644
--- a/CMakeExternals/Vigra.patch
+++ b/CMakeExternals/Vigra.patch
@@ -1,392 +1,519 @@
diff -urNb vigra-Version-1-10-0/CMakeLists.txt Vigra/CMakeLists.txt
--- vigra-Version-1-10-0/CMakeLists.txt 2013-11-18 17:48:16.000000000 +0100
+++ Vigra/CMakeLists.txt 2015-03-03 10:13:57.693725000 +0100
@@ -70,7 +70,12 @@ IF(WITH_OPENEXR)
ENDIF()
IF(WITH_HDF5)
+ FIND_PACKAGE(HDF5 PATHS ${HDF5_DIR} PATH_SUFFIXES hdf5 NO_DEFAULT_PATH NO_MODULE)
+ IF(NOT HDF5_FOUND)
VIGRA_FIND_PACKAGE(HDF5)
+ ENDIF()
+ # HDF5 changed their config, this ports the new style to the old style
+ set(HDF5_LIBRARIES ${HDF5_EXPORT_LIBRARIES})
ENDIF()
IF(WITH_BOOST_GRAPH)
@@ -395,3 +398,4 @@
ENDIF()
MESSAGE( STATUS "---------------------------------------------------------" )
+
diff -urNb vigra-Version-1-10-0/config/VigraConfig.cmake.in Vigra/config/VigraConfig.cmake.in
--- vigra-Version-1-10-0/config/VigraConfig.cmake.in 2013-11-18 17:48:16.000000000 +0100
+++ Vigra/config/VigraConfig.cmake.in 2015-03-03 10:13:57.693725000 +0100
@@ -1,7 +1,9 @@
get_filename_component(SELF_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
get_filename_component(Vigra_TOP_DIR "${SELF_DIR}/../../" ABSOLUTE)
-include(${SELF_DIR}/vigra-targets.cmake)
+if(NOT TARGET vigraimpex)
+ include(${SELF_DIR}/vigra-targets.cmake)
+endif()
get_target_property(VIGRA_TYPE vigraimpex TYPE)
if(${VIGRA_TYPE} STREQUAL "STATIC_LIBRARY")
SET(VIGRA_STATIC_LIB True)
@@ -12,3 +14,4 @@
IF(EXISTS ${SELF_DIR}/../vigranumpy/VigranumpyConfig.cmake)
INCLUDE(${SELF_DIR}/../vigranumpy/VigranumpyConfig.cmake)
ENDIF()
+
diff -urNb vigra-Version-1-10-0/include/vigra/random_forest/rf_common.hxx Vigra/include/vigra/random_forest/rf_common.hxx
--- vigra-Version-1-10-0/include/vigra/random_forest/rf_common.hxx 2013-11-18 17:48:16.000000000 +0100
+++ Vigra/include/vigra/random_forest/rf_common.hxx 2015-07-08 18:55:16.000000000 +0200
@@ -558,6 +558,7 @@
int is_weighted_; // class_weights_ are used
double precision_; // termination criterion for regression loss
int response_size_;
+ int max_tree_depth;
template<class T>
void to_classlabel(int index, T & out) const
@@ -583,7 +584,8 @@
EQUALS(class_weights_),
EQUALS(is_weighted_),
EQUALS(precision_),
- EQUALS(response_size_)
+ EQUALS(response_size_),
+ EQUALS(max_tree_depth)
{
std::back_insert_iterator<ArrayVector<Label_t> >
iter(classes);
@@ -604,7 +606,8 @@
EQUALS(class_weights_),
EQUALS(is_weighted_),
EQUALS(precision_),
- EQUALS(response_size_)
+ EQUALS(response_size_),
+ EQUALS(max_tree_depth)
{
std::back_insert_iterator<ArrayVector<Label_t> >
iter(classes);
@@ -624,7 +627,8 @@
EQUALS(used_);
EQUALS(is_weighted_);
EQUALS(precision_);
- EQUALS(response_size_)
+ EQUALS(response_size_);
+ EQUALS(max_tree_depth)
class_weights_.clear();
std::back_insert_iterator<ArrayVector<double> >
iter2(class_weights_);
@@ -648,7 +652,8 @@
EQUALS(used_);
EQUALS(is_weighted_);
EQUALS(precision_);
- EQUALS(response_size_)
+ EQUALS(response_size_);
+ EQUALS(max_tree_depth)
class_weights_.clear();
std::back_insert_iterator<ArrayVector<double> >
iter2(class_weights_);
@@ -677,7 +682,8 @@
COMPARE(used_);
COMPARE(class_weights_);
COMPARE(classes);
- COMPARE(response_size_)
+ COMPARE(response_size_);
+ COMPARE(max_tree_depth)
#undef COMPARE
return result;
}
@@ -715,6 +721,7 @@
PULL(used_, int);
PULL(precision_, double);
PULL(response_size_, int);
+ PULL(max_tree_depth, int);
if(is_weighted_)
{
vigra_precondition(end - begin == 10 + 2*class_count_,
@@ -747,6 +754,7 @@
PUSH(used_);
PUSH(precision_);
PUSH(response_size_);
+ PUSH(max_tree_depth);
if(is_weighted_)
{
std::copy(class_weights_.begin(),
@@ -773,6 +781,7 @@
PULL(used_, int);
PULL(precision_, double);
PULL(response_size_, int);
+ PULL(max_tree_depth, int);
class_weights_ = in["class_weights_"];
#undef PUSH
}
@@ -789,6 +798,7 @@
PUSH(used_);
PUSH(precision_);
PUSH(response_size_);
+ PUSH(max_tree_depth);
in["class_weights_"] = class_weights_;
#undef PUSH
}
@@ -805,7 +815,8 @@
used_(false),
is_weighted_(false),
precision_(0.0),
- response_size_(1)
+ response_size_(1),
+ max_tree_depth(50)
{}
@@ -857,7 +868,7 @@
is_weighted_ = false;
precision_ = 0.0;
response_size_ = 0;
-
+ max_tree_depth = 50;
}
bool used() const
diff -urNb vigra-Version-1-10-0/include/vigra/random_forest/rf_decisionTree.hxx Vigra/include/vigra/random_forest/rf_decisionTree.hxx
--- vigra-Version-1-10-0/include/vigra/random_forest/rf_decisionTree.hxx 2013-11-18 17:48:16.000000000 +0100
+++ Vigra/include/vigra/random_forest/rf_decisionTree.hxx 2015-07-08 18:55:16.000000000 +0200
@@ -90,6 +90,8 @@
ProblemSpec<> ext_param_;
unsigned int classCount_;
+ std::map<int, int> m_Parents;
+
public:
/* \brief Create tree with parameters */
@@ -350,6 +352,22 @@
continueLearn(features,labels,stack_entry,split,stop,visitor,randint);
}
+template < class TRandomForest>
+int GetTreeDepthForNode(int nodeIndex, TRandomForest* rf)
+{
+ int depth = 0;
+ while (true)
+ {
+ if (nodeIndex < 1)
+ {
+ break;
+ }
+ ++depth;
+ nodeIndex = rf->m_Parents[nodeIndex];
+ }
+ return depth;
+}
+
template < class U, class C,
class U2, class C2,
class StackEntry_t,
@@ -374,6 +392,11 @@
size_t last_node_pos = 0;
StackEntry_t top=stack.back();
+ Split_t* splitPointer = &split;
+ bool isDepthSplitter = true;
+
+ int maximumTreeDepth = splitPointer->GetMaximumTreeDepth();
+
while(!stack.empty())
{
@@ -392,7 +415,20 @@
//kind of node to make
TreeInt NodeID;
- if(stop(top))
+ bool depthStop = false;
+ if (isDepthSplitter)
+ {
+ int currentDepthLevel;
+ if (top.leftParent != StackEntry_t::DecisionTreeNoParent)
+ currentDepthLevel = GetTreeDepthForNode(top.leftParent, this);
+ else
+ currentDepthLevel = GetTreeDepthForNode(top.rightParent, this);
+
+ depthStop = (currentDepthLevel >= maximumTreeDepth);
+ }
+ if(stop(top) || (depthStop))
+
+ //if (stop(top) || currentDepthLevel >= MaximumSplitDepth(split))
NodeID = split.makeTerminalNode(features,
labels,
top,
@@ -421,17 +457,20 @@
// Using InteriorNodeBase because exact parameter form not needed.
// look at the Node base before getting scared.
last_node_pos = topology_.size();
+ m_Parents[last_node_pos] = StackEntry_t::DecisionTreeNoParent;
if(top.leftParent != StackEntry_t::DecisionTreeNoParent)
{
NodeBase(topology_,
parameters_,
top.leftParent).child(0) = last_node_pos;
+ m_Parents[last_node_pos] = top.leftParent;
}
else if(top.rightParent != StackEntry_t::DecisionTreeNoParent)
{
NodeBase(topology_,
parameters_,
top.rightParent).child(1) = last_node_pos;
+ m_Parents[last_node_pos] = top.rightParent;
}
diff -urNb vigra-Version-1-10-0/include/vigra/random_forest/rf_nodeproxy.hxx Vigra/include/vigra/random_forest/rf_nodeproxy.hxx
--- vigra-Version-1-10-0/include/vigra/random_forest/rf_nodeproxy.hxx 2013-11-18 17:48:16.000000000 +0100
+++ Vigra/include/vigra/random_forest/rf_nodeproxy.hxx 2015-07-08 18:55:16.000000000 +0200
@@ -50,7 +50,11 @@
namespace vigra
{
-
+class DepthSplitterBase
+{
+public:
+ virtual int GetMaximumTreeDepth() const = 0;
+};
enum NodeTags
{
diff -urNb vigra-Version-1-10-0/include/vigra/random_forest/rf_split.hxx Vigra/include/vigra/random_forest/rf_split.hxx
--- vigra-Version-1-10-0/include/vigra/random_forest/rf_split.hxx 2013-11-18 17:48:16.000000000 +0100
+++ Vigra/include/vigra/random_forest/rf_split.hxx 2015-07-08 18:55:16.000000000 +0200
@@ -108,6 +108,11 @@
\ref SplitBase::findBestSplit() or \ref SplitBase::makeTerminalNode().
**/
+ virtual int GetMaximumTreeDepth() const
+ {
+ return ext_param_.max_tree_depth;
+ }
+
template<class T>
void set_external_parameters(ProblemSpec<T> const & in)
{
diff -urNb vigra-Version-1-10-0/include/vigra/random_forest.hxx Vigra/include/vigra/random_forest.hxx
--- vigra-Version-1-10-0/include/vigra/random_forest.hxx 2013-11-18 17:48:16.000000000 +0100
+++ Vigra/include/vigra/random_forest.hxx 2015-07-15 14:08:18.548277915 +0200
@@ -165,6 +165,7 @@
ProblemSpec_t ext_param_;
/*mutable ArrayVector<int> tree_indices_;*/
rf::visitors::OnlineLearnVisitor online_visitor_;
+ bool multithreadPrediction; // enable/disable multithreaded predictProbabilities and predictLabels
void reset()
@@ -584,6 +585,18 @@
{
vigra_precondition(features.shape(0) == labels.shape(0),
"RandomForest::predictLabels(): Label array has wrong size.");
+ if (multithreadPrediction)
+ {
+#pragma omp parallel for
+ for(int k=0; k<features.shape(0); ++k)
+ {
+ vigra_precondition(!detail::contains_nan(rowVector(features, k)),
+ "RandomForest::predictLabels(): NaN in feature matrix.");
+ labels(k,0) = detail::RequiresExplicitCast<T>::cast(predictLabel(rowVector(features, k), rf_default()));
+ }
+ }
+ else
+ {
for(int k=0; k<features.shape(0); ++k)
{
vigra_precondition(!detail::contains_nan(rowVector(features, k)),
@@ -591,6 +604,7 @@
labels(k,0) = detail::RequiresExplicitCast<T>::cast(predictLabel(rowVector(features, k), rf_default()));
}
}
+ }
/** \brief predict multiple labels with given features
*
@@ -1261,6 +1275,60 @@
}
*/
//Classify for each row.
+ if (multithreadPrediction)
+ {
+#pragma omp parallel for
+ for(int row=0; row < rowCount(features); ++row)
+ {
+ MultiArrayView<2, U, StridedArrayTag> currentRow(rowVector(features, row));
+
+ // when the features contain an NaN, the instance doesn't belong to any class
+ // => indicate this by returning a zero probability array.
+ if(detail::contains_nan(currentRow))
+ {
+ rowVector(prob, row).init(0.0);
+ continue;
+ }
+
+ ArrayVector<double>::const_iterator weights;
+
+ //totalWeight == totalVoteCount!
+ double totalWeight = 0.0;
+
+ //Let each tree classify...
+ for(int k=0; k<options_.tree_count_; ++k)
+ {
+ //get weights predicted by single tree
+ weights = trees_[k /*tree_indices_[k]*/].predict(currentRow);
+
+ //update votecount.
+ int weighted = options_.predict_weighted_;
+ for(int l=0; l<ext_param_.class_count_; ++l)
+ {
+ double cur_w = weights[l] * (weighted * (*(weights-1))
+ + (1-weighted));
+ prob(row, l) += (T)cur_w;
+ //every weight in totalWeight.
+ totalWeight += cur_w;
+ }
+ if(stop.after_prediction(weights,
+ k,
+ rowVector(prob, row),
+ totalWeight))
+ {
+ break;
+ }
+ }
+
+ //Normalise votes in each row by total VoteCount (totalWeight
+ for(int l=0; l< ext_param_.class_count_; ++l)
+ {
+ prob(row, l) /= detail::RequiresExplicitCast<T>::cast(totalWeight);
+ }
+ }
+ }
+ else
+ {
for(int row=0; row < rowCount(features); ++row)
{
MultiArrayView<2, U, StridedArrayTag> currentRow(rowVector(features, row));
@@ -1309,6 +1377,7 @@
prob(row, l) /= detail::RequiresExplicitCast<T>::cast(totalWeight);
}
}
+ }
}
diff -u -r Vigra.orig/include/vigra/imagecontainer.hxx Vigra/include/vigra/imagecontainer.hxx
--- Vigra.orig/include/vigra/imagecontainer.hxx 2013-11-18 17:48:16.000000000 +0100
+++ Vigra/include/vigra/imagecontainer.hxx 2017-08-26 11:02:40.079669325 +0200
@@ -760,7 +760,7 @@
/** swap contents of this array with the contents of other
(STL-Container interface)
*/
- void swap(const ImagePyramid<ImageType, Alloc> &other)
+ void swap(ImagePyramid<ImageType, Alloc> &other)
{
images_.swap(other.images_);
std::swap(lowestLevel_, other.lowestLevel_);
+diff -urNb vigra-Version-1-10-0/include/vigra/functortraits.hxx Vigra/include/vigra/functortraits.hxx
+--- vigra-Version-1-10-0/include/vigra/functortraits.hxx 2013-11-18 17:48:16.000000000 +0100
++++ Vigra/include/vigra/functortraits.hxx 2022-03-17 15:15:01.637026200 +0100
+@@ -195,8 +195,6 @@
+ VIGRA_DEFINE_STL_FUNCTOR(std::negate, VigraTrueType, VigraFalseType)
+ VIGRA_DEFINE_STL_FUNCTOR(std::logical_not, VigraTrueType, VigraFalseType)
+ VIGRA_DEFINE_STL_FUNCTOR(std::unary_negate, VigraTrueType, VigraFalseType)
+-VIGRA_DEFINE_STL_FUNCTOR(std::binder1st, VigraTrueType, VigraFalseType)
+-VIGRA_DEFINE_STL_FUNCTOR(std::binder2nd, VigraTrueType, VigraFalseType)
+ #undef VIGRA_DEFINE_STL_FUNCTOR
+
+ template <class R>
+diff -urNb vigra-Version-1-10-0/src/impex/imageinfo.cxx Vigra/src/impex/imageinfo.cxx
+--- vigra-Version-1-10-0/src/impex/imageinfo.cxx 2013-11-18 17:48:16.000000000 +0100
++++ Vigra/src/impex/imageinfo.cxx 2022-03-17 15:22:20.975223000 +0100
+@@ -1016,7 +1016,7 @@
+ do
+ {
+ numEndIt = std::find_if(numBeginIt, filename.rend(),(int (*)(int)) &isdigit);
+- numBeginIt = std::find_if(numEndIt, filename.rend(), not1(std::ptr_fun((int (*)(int))&isdigit)));
++ numBeginIt = std::find_if(numEndIt, filename.rend(), std::not_fn((int (*)(int))&isdigit));
+
+ if(numEndIt != filename.rend())
+ {
+diff -urNb vigra-1.10.0-src/include/vigra/separableconvolution.hxx Vigra/include/vigra/separableconvolution.hxx
+--- vigra-1.10.0-src/include/vigra/separableconvolution.hxx 2013-11-18 17:48:16.000000000 +0100
++++ Vigra/include/vigra/separableconvolution.hxx 2022-03-17 23:08:05.690694437 +0100
+@@ -1409,9 +1409,6 @@
+ {}
+
+ ~InitProxy()
+-#ifndef _MSC_VER
+- throw(PreconditionViolation)
+-#endif
+ {
+ vigra_precondition(count_ == 1 || count_ == sum_,
+ "Kernel1D::initExplicitly(): "
+diff -urNb vigra-1.10.0-src/src/impex/gif.cxx Vigra/src/impex/gif.cxx
+--- vigra-1.10.0-src/src/impex/gif.cxx 2013-11-18 17:48:16.000000000 +0100
++++ Vigra/src/impex/gif.cxx 2022-03-17 23:17:05.896364451 +0100
+@@ -482,12 +482,12 @@
+ in_code,
+ old_code;
+
+- register int
++ int
+ bits,
+ code,
+ count;
+
+- register unsigned long
++ unsigned long
+ datum;
+
+ void_vector<Int16> prefix(MaxStackSize);
+@@ -496,8 +496,8 @@
+ void_vector<UInt8> packet(256);
+ void_vector<UInt16> indices(header.width*header.height);
+
+- register UInt8 *c;
+- register UInt16 *p = indices.begin();
++ UInt8 *c;
++ UInt16 *p = indices.begin();
+
+ UInt8
+ data_size,
+@@ -624,7 +624,7 @@
+
+ int pass, x, y;
+
+- register UInt16 *q;
++ UInt16 *q;
+
+ static int
+ interlace_rate[4] = { 8, 8, 4, 2 },
+@@ -891,9 +891,9 @@
+ long
+ datum;
+
+- register int k;
++ int k;
+
+- register UInt8 *p;
++ UInt8 *p;
+
+ void_vector<Int16> hash_code(MaxHashTable);
+ void_vector<Int16> hash_prefix(MaxHashTable);
+diff -urNb vigra-1.10.0-src/include/vigra/random_forest/rf_algorithm.hxx Vigra/include/vigra/random_forest/rf_algorithm.hxx
+--- vigra-1.10.0-src/include/vigra/random_forest/rf_algorithm.hxx 2013-11-18 17:48:16.000000000 +0100
++++ Vigra/include/vigra/random_forest/rf_algorithm.hxx 2022-03-18 08:52:17.519102162 +0100
+@@ -34,6 +34,7 @@
+ /************************************************************************/
+ #define VIGRA_RF_ALGORITHM_HXX
+
++#include <random>
+ #include <vector>
+ #include "splices.hxx"
+ #include <queue>
+@@ -1151,7 +1152,7 @@
+ ArrayVector<Int32> indices(pr.features().shape(0));
+ for(int ii = 0; ii < pr.features().shape(0); ++ii)
+ indices.push_back(ii);
+- std::random_shuffle(indices.begin(), indices.end());
++ std::shuffle(indices.begin(), indices.end(), std::mt19937(std::random_device()()));
+ for(int ii = 0; ii < rf.ext_param_.row_count_; ++ii)
+ {
+ if(!sm.is_used()[indices[ii]] && cts[pr.response()(indices[ii], 0)] < 3000)
+diff -urNb vigra-1.10.0-src/include/vigra/random_forest/rf_visitors.hxx Vigra/include/vigra/random_forest/rf_visitors.hxx
+--- vigra-1.10.0-src/include/vigra/random_forest/rf_visitors.hxx 2013-11-18 17:48:16.000000000 +0100
++++ Vigra/include/vigra/random_forest/rf_visitors.hxx 2022-03-18 08:54:14.449776895 +0100
+@@ -41,6 +41,7 @@
+ #include <vigra/windows.h>
+ #include <iostream>
+ #include <iomanip>
++#include <random>
+
+ #include <vigra/multi_pointoperators.hxx>
+ #include <vigra/timing.hxx>
+@@ -909,7 +910,7 @@
+ {
+ ArrayVector<int> oob_indices;
+ ArrayVector<int> cts(class_count, 0);
+- std::random_shuffle(indices.begin(), indices.end());
++ std::shuffle(indices.begin(), indices.end(), std::mt19937(std::random_device()()));
+ for(int ii = 0; ii < rf.ext_param_.row_count_; ++ii)
+ {
+ if(!sm.is_used()[indices[ii]] && cts[pr.response()(indices[ii], 0)] < 40000)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d77d4c55b2..062bb7cb9c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,1443 +1,1421 @@
#[[ When increasing the minimum required version, check if Boost_ADDITIONAL_VERSIONS
in CMake/PackageDepends/MITK_Boost_Config.cmake can be removed. See the first
long comment in CMakeExternals/Boost.cmake for details. ]]
set(MITK_CMAKE_MINIMUM_REQUIRED_VERSION 3.18)
cmake_minimum_required(VERSION ${MITK_CMAKE_MINIMUM_REQUIRED_VERSION})
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19 AND CMAKE_VERSION VERSION_LESS 3.19.2)
message(FATAL_ERROR "\
CMake v${CMAKE_VERSION} is defective [1]. \
Please either downgrade to v3.18 or upgrade to at least v3.19.2.\n\
[1] https://gitlab.kitware.com/cmake/cmake/-/issues/21529")
endif()
#-----------------------------------------------------------------------------
# Policies
#-----------------------------------------------------------------------------
#[[ T28060
https://cmake.org/cmake/help/v3.18/policy/CMP0091.html
https://cmake.org/cmake/help/v3.18/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html
We pass CMP0091 to all external projects as command-line argument:
-DCMAKE_POLICY_DEFAULT_CMP0091:STRING=OLD
]]
cmake_policy(SET CMP0091 OLD)
#-----------------------------------------------------------------------------
# Superbuild Option - Enabled by default
#-----------------------------------------------------------------------------
option(MITK_USE_SUPERBUILD "Build MITK and the projects it depends on via SuperBuild.cmake." ON)
if(MITK_USE_SUPERBUILD)
project(MITK-superbuild)
set(MITK_SOURCE_DIR ${PROJECT_SOURCE_DIR})
set(MITK_BINARY_DIR ${PROJECT_BINARY_DIR})
else()
project(MITK VERSION 2021.10.99)
include_directories(SYSTEM ${MITK_SUPERBUILD_BINARY_DIR})
endif()
#-----------------------------------------------------------------------------
# MITK Extension Feature
#-----------------------------------------------------------------------------
set(MITK_EXTENSION_DIRS "" CACHE STRING "")
unset(MITK_ABSOLUTE_EXTENSION_DIRS)
foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS})
get_filename_component(MITK_ABSOLUTE_EXTENSION_DIR "${MITK_EXTENSION_DIR}" ABSOLUTE)
list(APPEND MITK_ABSOLUTE_EXTENSION_DIRS "${MITK_ABSOLUTE_EXTENSION_DIR}")
endforeach()
set(MITK_DIR_PLUS_EXTENSION_DIRS "${MITK_SOURCE_DIR}" ${MITK_ABSOLUTE_EXTENSION_DIRS})
#-----------------------------------------------------------------------------
# Update CMake module path
#-----------------------------------------------------------------------------
set(MITK_CMAKE_DIR ${MITK_SOURCE_DIR}/CMake)
set(CMAKE_MODULE_PATH ${MITK_CMAKE_DIR})
foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS})
set(MITK_CMAKE_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMake")
if(EXISTS "${MITK_CMAKE_EXTENSION_DIR}")
list(APPEND CMAKE_MODULE_PATH "${MITK_CMAKE_EXTENSION_DIR}")
endif()
endforeach()
#-----------------------------------------------------------------------------
# CMake function(s) and macro(s)
#-----------------------------------------------------------------------------
# Standard CMake macros
include(FeatureSummary)
include(CTest)
include(CMakeParseArguments)
include(FindPackageHandleStandardArgs)
# MITK macros
include(mitkFunctionGetGccVersion)
include(mitkFunctionCheckCompilerFlags)
include(mitkFunctionSuppressWarnings) # includes several functions
include(mitkMacroEmptyExternalProject)
include(mitkFunctionEnableBuildConfiguration)
include(mitkFunctionWhitelists)
include(mitkFunctionAddExternalProject)
include(mitkFunctionAddLibrarySearchPaths)
SUPPRESS_VC_DEPRECATED_WARNINGS()
#-----------------------------------------------------------------------------
# Set a default build type if none was specified
#-----------------------------------------------------------------------------
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to 'Debug' as none was specified.")
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE)
# Set the possible values of build type for cmake-gui
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY
STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
#-----------------------------------------------------------------------------
# Check miminum macOS version
#-----------------------------------------------------------------------------
# The minimum supported macOS version is 10.14. If you use a version less than
# 10.14, there is no guarantee that the build still works.
if(APPLE)
exec_program(sw_vers ARGS -productVersion OUTPUT_VARIABLE macos_version)
if (macos_version VERSION_LESS "10.14")
message(WARNING "Detected macOS version \"${macos_version}\" is not supported anymore. Minimum required macOS version is at least 10.14.")
endif()
if (CMAKE_OSX_DEPLOYMENT_TARGET AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS 10.14)
message(WARNING "Detected macOS deployment target \"${CMAKE_OSX_DEPLOYMENT_TARGET}\" is not supported anymore. Minimum required macOS version is at least 10.14.")
endif()
endif()
#-----------------------------------------------------------------------------
# Check miminum compiler versions
#-----------------------------------------------------------------------------
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
# require at least gcc 4.9 as provided by ppa:ubuntu-toolchain-r/test for Ubuntu 14.04
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
message(FATAL_ERROR "GCC version must be at least 4.9
If you are using Ubuntu 14.04, you can easily install gcc and g++ 4.9 (or any later version available) in addition to your version ${CMAKE_CXX_COMPILER_VERSION}:
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install gcc-4.9 g++-4.9
Make sure to explicitly specify these compilers when configuring MITK:
CMAKE_C_COMPILER:FILEPATH=/usr/bin/gcc-4.9
CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/g++-4.9
For more information on the proposed PPA see the Toolchain Updates section of https://wiki.ubuntu.com/ToolChain.")
endif()
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
# require at least clang 3.4
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.4)
message(FATAL_ERROR "Clang version must be at least 3.4")
endif()
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
# require at least clang 5.0
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
message(FATAL_ERROR "Apple Clang version must be at least 5.0")
endif()
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# require at least Visual Studio 2017
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.10)
message(FATAL_ERROR "Microsoft Visual Studio 2017 or newer required")
endif()
else()
message(WARNING "You are using an unsupported compiler! Compilation has only been tested with Clang (Linux or Apple), GCC and MSVC.")
endif()
if(CMAKE_COMPILER_IS_GNUCXX)
mitkFunctionGetGccVersion(${CMAKE_CXX_COMPILER} GCC_VERSION)
else()
set(GCC_VERSION 0)
endif()
-set(MITK_CXX_STANDARD 14)
+set(MITK_CXX_STANDARD 17)
set(CMAKE_CXX_EXTENSIONS 0)
set(CMAKE_CXX_STANDARD ${MITK_CXX_STANDARD})
set(CMAKE_CXX_STANDARD_REQUIRED 1)
# This is necessary to avoid problems with compile feature checks.
-# CMAKE_CXX_STANDARD seems to only set the -std=c++14 flag for targets.
-# However, compile flag checks also need to be done with -std=c++14.
-# The MITK_CXX14_FLAG variable is also used for external projects
+# CMAKE_CXX_STANDARD seems to only set the -std=c++<std> flag for targets.
+# However, compile flag checks also need to be done with -std=c++<std>.
+# The MITK_CXX<std>_FLAG variable is also used for external projects
# build during the MITK super-build.
-mitkFunctionCheckCompilerFlags("-std=c++14" MITK_CXX14_FLAG)
+mitkFunctionCheckCompilerFlags("-std=c++${MITK_CXX_STANDARD}" MITK_CXX${MITK_CXX_STANDARD}_FLAG)
#-----------------------------------------------------------------------------
# Warn if source or build path is too long
#-----------------------------------------------------------------------------
if(WIN32)
set(_src_dir_length_max 50)
set(_bin_dir_length_max 50)
if(MITK_USE_SUPERBUILD)
set(_src_dir_length_max 34) # _src_dir_length_max - strlen(ep/src/ITK-build)
set(_bin_dir_length_max 40) # _bin_dir_length_max - strlen(MITK-build)
endif()
string(LENGTH "${MITK_SOURCE_DIR}" _src_n)
string(LENGTH "${MITK_BINARY_DIR}" _bin_n)
# The warnings should be converted to errors
if(_src_n GREATER _src_dir_length_max)
message(WARNING "MITK source code directory path length is too long (${_src_n} > ${_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()
#-----------------------------------------------------------------------------
# Additional MITK Options (also shown during superbuild)
#-----------------------------------------------------------------------------
# -----------------------------------------
# General build options
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_FAST_TESTING "Disable long-running tests like packaging" OFF)
option(MITK_XVFB_TESTING "Execute test drivers through xvfb-run" OFF)
option(MITK_BUILD_ALL_APPS "Build all MITK applications" OFF)
option(MITK_BUILD_EXAMPLES "Build the MITK Examples" OFF)
mark_as_advanced(
MITK_XVFB_TESTING
MITK_FAST_TESTING
MITK_BUILD_ALL_APPS
)
#-----------------------------------------------------------------------------
# Set UI testing flags
#-----------------------------------------------------------------------------
if(MITK_XVFB_TESTING)
set(MITK_XVFB_TESTING_COMMAND "xvfb-run" "--auto-servernum" CACHE STRING "Command and options to test through Xvfb")
mark_as_advanced(MITK_XVFB_TESTING_COMMAND)
endif(MITK_XVFB_TESTING)
# -----------------------------------------
# Other options
set(MITK_CUSTOM_REVISION_DESC "" CACHE STRING "Override MITK revision description")
mark_as_advanced(MITK_CUSTOM_REVISION_DESC)
set_property(GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS "")
include(CMakeExternals/ExternalProjectList.cmake)
foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS})
set(MITK_CMAKE_EXTERNALS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMakeExternals")
if(EXISTS "${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/ExternalProjectList.cmake")
include("${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/ExternalProjectList.cmake")
endif()
endforeach()
# -----------------------------------------
# Other MITK_USE_* options not related to
# external projects build via the
# MITK superbuild
option(MITK_USE_BLUEBERRY "Build the BlueBerry platform" ON)
option(MITK_USE_OpenCL "Use OpenCL GPU-Computing library" OFF)
option(MITK_USE_OpenMP "Use OpenMP" OFF)
option(MITK_USE_Python3 "Use Python 3" OFF)
#-----------------------------------------------------------------------------
# Build configurations
#-----------------------------------------------------------------------------
set(_buildConfigs "Custom")
file(GLOB _buildConfigFiles CMake/BuildConfigurations/*.cmake)
foreach(_buildConfigFile ${_buildConfigFiles})
get_filename_component(_buildConfigFile ${_buildConfigFile} NAME_WE)
list(APPEND _buildConfigs ${_buildConfigFile})
endforeach()
foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS})
file(GLOB _extBuildConfigFiles "${MITK_EXTENSION_DIR}/CMake/BuildConfigurations/*.cmake")
foreach(_extBuildConfigFile ${_extBuildConfigFiles})
get_filename_component(_extBuildConfigFile "${_extBuildConfigFile}" NAME_WE)
list(APPEND _buildConfigs "${_extBuildConfigFile}")
endforeach()
list(REMOVE_DUPLICATES _buildConfigs)
endforeach()
set(MITK_BUILD_CONFIGURATION "Custom" CACHE STRING "Use pre-defined MITK configurations")
set_property(CACHE MITK_BUILD_CONFIGURATION PROPERTY STRINGS ${_buildConfigs})
mitkFunctionEnableBuildConfiguration()
mitkFunctionCreateWhitelistPaths(MITK)
mitkFunctionFindWhitelists(MITK)
# -----------------------------------------
# Qt version related variables
option(MITK_USE_Qt5 "Use Qt 5 library" ON)
if(MITK_USE_Qt5)
if(WIN32)
set(MITK_QT5_MINIMUM_VERSION 5.12.9)
else()
set(MITK_QT5_MINIMUM_VERSION 5.12)
endif()
set(MITK_QT5_COMPONENTS Concurrent OpenGL PrintSupport Script Sql Svg Widgets Xml XmlPatterns WebEngineWidgets UiTools Help LinguistTools)
if(APPLE)
list(APPEND MITK_QT5_COMPONENTS DBus)
elseif(UNIX)
list(APPEND MITK_QT5_COMPONENTS X11Extras)
endif()
# Hint at default install locations of Qt
if(NOT Qt5_DIR)
if(MSVC)
set(_dir_candidates "C:/Qt")
if(CMAKE_GENERATOR MATCHES "^Visual Studio [0-9]+ ([0-9]+)")
set(_compilers "msvc${CMAKE_MATCH_1}")
elseif(CMAKE_GENERATOR MATCHES "Ninja")
include(mitkFunctionGetMSVCVersion)
mitkFunctionGetMSVCVersion()
if(VISUAL_STUDIO_PRODUCT_NAME MATCHES "^Visual Studio ([0-9]+)")
set(_compilers "msvc${CMAKE_MATCH_1}")
endif()
endif()
if(_compilers MATCHES "[0-9]+")
if (CMAKE_MATCH_0 EQUAL 2022)
list(APPEND _compilers "msvc2019" "msvc2017") # Binary compatible
elseif (CMAKE_MATCH_0 EQUAL 2019)
list(APPEND _compilers "msvc2017") # Binary compatible
endif()
endif()
else()
set(_dir_candidates ~/Qt)
if(APPLE)
set(_compilers clang)
else()
list(APPEND _dir_candidates /opt/Qt)
set(_compilers gcc)
endif()
endif()
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
foreach(_compiler ${_compilers})
list(APPEND _compilers64 "${_compiler}_64")
endforeach()
set(_compilers ${_compilers64})
endif()
foreach(_dir_candidate ${_dir_candidates})
get_filename_component(_dir_candidate ${_dir_candidate} REALPATH)
foreach(_compiler ${_compilers})
set(_glob_expression "${_dir_candidate}/5.*/${_compiler}")
file(GLOB _hints ${_glob_expression})
list(SORT _hints)
list(APPEND MITK_QT5_HINTS ${_hints})
endforeach()
endforeach()
endif()
find_package(Qt5 ${MITK_QT5_MINIMUM_VERSION} COMPONENTS ${MITK_QT5_COMPONENTS} REQUIRED HINTS ${MITK_QT5_HINTS})
endif()
# -----------------------------------------
# Custom dependency logic
if(WIN32 AND Qt5_DIR)
set(_dir_candidate "${Qt5_DIR}/../../../../../Tools/OpenSSL/Win_x64")
get_filename_component(_dir_candidate ${_dir_candidate} ABSOLUTE)
if(EXISTS "${_dir_candidate}")
set(OPENSSL_ROOT_DIR "${_dir_candidate}")
endif()
endif()
find_package(OpenSSL)
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")
if(MITK_USE_cpprestsdk)
if(NOT OpenSSL_FOUND)
set(openssl_message "Could not find OpenSSL (dependency of C++ REST SDK).\n")
if(UNIX)
if(APPLE)
set(openssl_message "${openssl_message}Please install it using your favorite package management "
"system (i.e. Homebrew or MacPorts).\n")
else()
set(openssl_message "${openssl_message}Please install the dev package of OpenSSL (i.e. libssl-dev).\n")
endif()
else()
set(openssl_message "${openssl_message}Please either install Win32 OpenSSL:\n"
" https://slproweb.com/products/Win32OpenSSL.html\n"
"Or use the Qt Maintenance tool to install:\n"
" Developer and Designer Tools > OpenSSL Toolkit > OpenSSL 64-bit binaries\n")
endif()
set(openssl_message "${openssl_message}If it still cannot be found, you can hint CMake to find OpenSSL by "
"adding/setting the OPENSSL_ROOT_DIR variable to the root directory of an "
"OpenSSL installation. Make sure to clear variables of partly found "
"versions of OpenSSL before, or they will be mixed up.")
message(FATAL_ERROR ${openssl_message})
endif()
list(APPEND MITK_USE_Boost_LIBRARIES date_time regex system)
if(UNIX)
list(APPEND MITK_USE_Boost_LIBRARIES atomic chrono filesystem random thread)
endif()
list(REMOVE_DUPLICATES MITK_USE_Boost_LIBRARIES)
set(MITK_USE_Boost_LIBRARIES ${MITK_USE_Boost_LIBRARIES} CACHE STRING "A semi-colon separated list of required Boost libraries" FORCE)
endif()
if(MITK_USE_Python3)
set(MITK_USE_ZLIB ON CACHE BOOL "" FORCE)
if(APPLE AND CMAKE_FRAMEWORK_PATH AND CMAKE_FRAMEWORK_PATH MATCHES "python3\\.?([0-9]+)")
find_package(Python3 3.${CMAKE_MATCH_1} EXACT REQUIRED COMPONENTS Interpreter Development NumPy)
else()
find_package(Python3 REQUIRED COMPONENTS Interpreter Development NumPy)
endif()
if(WIN32)
string(REPLACE "\\" "/" Python3_STDARCH "${Python3_STDARCH}")
string(REPLACE "\\" "/" Python3_STDLIB "${Python3_STDLIB}")
string(REPLACE "\\" "/" Python3_SITELIB "${Python3_SITELIB}")
endif()
endif()
if(BUILD_TESTING AND NOT MITK_USE_CppUnit)
message("> Forcing MITK_USE_CppUnit to ON because BUILD_TESTING=ON")
set(MITK_USE_CppUnit ON CACHE BOOL "Use CppUnit for unit tests" FORCE)
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()
#-----------------------------------------------------------------------------
# Pixel type multiplexing
#-----------------------------------------------------------------------------
# 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<unsigned char>, itk::RGBAPixel<unsigned char>"
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<unsigned char>, itk::RGBAPixel<unsigned char>"
CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE)
endif()
if(NOT MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES)
string(REPLACE "," ";" _integral_types ${MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES})
string(REPLACE "," ";" _floating_types ${MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES})
foreach(_scalar_type ${_integral_types} ${_floating_types})
set(MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES
"${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}itk::VariableLengthVector<${_scalar_type}>,")
endforeach()
string(LENGTH "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}" _length)
math(EXPR _length "${_length} - 1")
string(SUBSTRING "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}" 0 ${_length} MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES)
set(MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES ${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}
CACHE STRING "List of vector pixel types used in AccessByItk and InstantiateAccessFunction macros for itk::VectorImage types" 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()
find_package(Git REQUIRED)
#-----------------------------------------------------------------------------
# Superbuild script
#-----------------------------------------------------------------------------
if(MITK_USE_SUPERBUILD)
include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake")
# Print configuration summary
message("\n\n")
feature_summary(
DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------"
WHAT ALL)
return()
endif()
#*****************************************************************************
#**************************** END OF SUPERBUILD ****************************
#*****************************************************************************
#-----------------------------------------------------------------------------
# Organize MITK targets in folders
#-----------------------------------------------------------------------------
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(MITK_ROOT_FOLDER "MITK" CACHE STRING "")
mark_as_advanced(MITK_ROOT_FOLDER)
#-----------------------------------------------------------------------------
# CMake function(s) and macro(s)
#-----------------------------------------------------------------------------
include(WriteBasicConfigVersionFile)
include(CheckCXXSourceCompiles)
include(GenerateExportHeader)
include(mitkFunctionAddCustomModuleTest)
include(mitkFunctionCheckModuleDependencies)
include(mitkFunctionCompileSnippets)
include(mitkFunctionConfigureVisualStudioUserProjectFile)
include(mitkFunctionCreateBlueBerryApplication)
include(mitkFunctionCreateCommandLineApp)
include(mitkFunctionCreateModule)
include(mitkFunctionCreatePlugin)
include(mitkFunctionCreateProvisioningFile)
include(mitkFunctionGetLibrarySearchPaths)
include(mitkFunctionGetVersion)
include(mitkFunctionGetVersionDescription)
include(mitkFunctionInstallAutoLoadModules)
include(mitkFunctionInstallCTKPlugin)
include(mitkFunctionInstallProvisioningFiles)
include(mitkFunctionInstallThirdPartyCTKPlugins)
include(mitkFunctionOrganizeSources)
include(mitkFunctionUseModules)
if( ${MITK_USE_MatchPoint} )
include(mitkFunctionCreateMatchPointDeployedAlgorithm)
endif()
include(mitkMacroConfigureItkPixelTypes)
include(mitkMacroCreateExecutable)
include(mitkMacroCreateModuleTests)
include(mitkMacroGenerateToolsLibrary)
include(mitkMacroGetLinuxDistribution)
include(mitkMacroGetPMDPlatformString)
include(mitkMacroInstall)
include(mitkMacroInstallHelperApp)
include(mitkMacroInstallTargets)
include(mitkMacroMultiplexPicType)
# Deprecated
include(mitkMacroCreateCTKPlugin)
#-----------------------------------------------------------------------------
# Global CMake variables
#-----------------------------------------------------------------------------
-# Required and enabled C++14 features for all MITK code.
-# These are added as PUBLIC compile features to all MITK modules.
-set(MITK_CXX_FEATURES
- cxx_auto_type
- cxx_decltype
- cxx_enum_forward_declarations
- cxx_extended_friend_declarations
- cxx_extern_templates
- cxx_final
- cxx_lambdas
- cxx_local_type_template_args
- cxx_long_long_type
- cxx_nullptr
- cxx_override
- cxx_range_for
- cxx_right_angle_brackets
- cxx_rvalue_references
- cxx_static_assert
- cxx_strong_enums
- cxx_template_template_parameters
- cxx_trailing_return_types
- cxx_variadic_macros
-)
-
if(NOT DEFINED CMAKE_DEBUG_POSTFIX)
# We can't do this yet because the CTK Plugin Framework
# cannot cope with a postfix yet.
#set(CMAKE_DEBUG_POSTFIX d)
endif()
#-----------------------------------------------------------------------------
# Output directories.
#-----------------------------------------------------------------------------
set(_default_LIBRARY_output_dir lib)
set(_default_RUNTIME_output_dir bin)
set(_default_ARCHIVE_output_dir lib)
foreach(type LIBRARY RUNTIME ARCHIVE)
# Make sure the directory exists
if(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_CMAKE_${type}_OUTPUT_DIRECTORY)
set(CMAKE_${type}_OUTPUT_DIRECTORY ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY})
else()
set(CMAKE_${type}_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${_default_${type}_output_dir})
set(MITK_CMAKE_${type}_OUTPUT_DIRECTORY ${CMAKE_${type}_OUTPUT_DIRECTORY})
endif()
set(CMAKE_${type}_OUTPUT_DIRECTORY ${CMAKE_${type}_OUTPUT_DIRECTORY} CACHE INTERNAL "Output directory for ${type} files.")
mark_as_advanced(CMAKE_${type}_OUTPUT_DIRECTORY)
endforeach()
#-----------------------------------------------------------------------------
# Set MITK specific options and variables (NOT available during superbuild)
#-----------------------------------------------------------------------------
if(OpenSSL_FOUND AND WIN32)
set(MITK_OPENSSL_SSL_DLL "" CACHE FILEPATH "")
set(MITK_OPENSSL_CRYPTO_DLL "" CACHE FILEPATH "")
if(MITK_OPENSSL_SSL_DLL AND EXISTS "${MITK_OPENSSL_SSL_DLL}" AND MITK_OPENSSL_CRYPTO_DLL AND EXISTS "${MITK_OPENSSL_CRYPTO_DLL}")
foreach(config_type ${CMAKE_CONFIGURATION_TYPES})
execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${MITK_BINARY_DIR}/bin/${config_type}")
configure_file("${MITK_OPENSSL_SSL_DLL}" "${MITK_BINARY_DIR}/bin/${config_type}/" COPYONLY)
configure_file("${MITK_OPENSSL_CRYPTO_DLL}" "${MITK_BINARY_DIR}/bin/${config_type}/" COPYONLY)
endforeach()
MITK_INSTALL(FILES
"${MITK_OPENSSL_SSL_DLL}"
"${MITK_OPENSSL_CRYPTO_DLL}"
)
endif()
endif()
# Look for optional Doxygen package
find_package(Doxygen)
option(BLUEBERRY_DEBUG_SMARTPOINTER "Enable code for debugging smart pointers" OFF)
mark_as_advanced(BLUEBERRY_DEBUG_SMARTPOINTER)
# Ask the user to show the console window for applications
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)
if(NOT MITK_FAST_TESTING)
if(MITK_CTEST_SCRIPT_MODE STREQUAL "Continuous" OR MITK_CTEST_SCRIPT_MODE STREQUAL "Experimental")
set(MITK_FAST_TESTING ON)
endif()
endif()
if(NOT UNIX)
set(MITK_WIN32_FORCE_STATIC "STATIC" CACHE INTERNAL "Use this variable to always build static libraries on non-unix platforms")
endif()
if(MITK_BUILD_ALL_PLUGINS)
set(MITK_BUILD_ALL_PLUGINS_OPTION "FORCE_BUILD_ALL")
endif()
# Configure pixel types used for ITK image access multiplexing
mitkMacroConfigureItkPixelTypes()
# Configure module naming conventions
set(MITK_MODULE_NAME_REGEX_MATCH "^[A-Z].*$")
set(MITK_MODULE_NAME_REGEX_NOT_MATCH "^[Mm][Ii][Tt][Kk].*$")
set(MITK_DEFAULT_MODULE_NAME_PREFIX "Mitk")
set(MITK_MODULE_NAME_PREFIX ${MITK_DEFAULT_MODULE_NAME_PREFIX})
set(MITK_MODULE_NAME_DEFAULTS_TO_DIRECTORY_NAME 1)
#-----------------------------------------------------------------------------
# Get MITK version info
#-----------------------------------------------------------------------------
mitkFunctionGetVersion(${MITK_SOURCE_DIR} MITK)
mitkFunctionGetVersionDescription(${MITK_SOURCE_DIR} MITK)
# MITK_VERSION
set(MITK_VERSION_STRING "${MITK_VERSION_MAJOR}.${MITK_VERSION_MINOR}.${MITK_VERSION_PATCH}")
if(MITK_VERSION_PATCH STREQUAL "99")
set(MITK_VERSION_STRING "${MITK_VERSION_STRING}-${MITK_REVISION_SHORTID}")
endif()
#-----------------------------------------------------------------------------
# Installation preparation
#
# These should be set before any MITK install macros are used
#-----------------------------------------------------------------------------
# on macOS all BlueBerry plugins get copied into every
# application bundle (.app directory) specified here
if(MITK_USE_BLUEBERRY AND APPLE)
foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS})
set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications")
if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake")
set(MITK_APPS "")
include("${MITK_APPLICATIONS_EXTENSION_DIR}/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} Mitk${app_name})
endif()
endforeach()
endif()
endforeach()
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 "${COVERAGE_CXX_FLAGS} ${MITK_CXX14_FLAG}")
+set(MITK_CXX_FLAGS "${COVERAGE_CXX_FLAGS} ${MITK_CXX${MITK_CXX_STANDARD}_FLAG}")
set(MITK_CXX_FLAGS_DEBUG )
set(MITK_CXX_FLAGS_RELEASE )
set(MITK_EXE_LINKER_FLAGS )
set(MITK_SHARED_LINKER_FLAGS )
if(WIN32)
set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -DPOCO_NO_UNWINDOWS -DWIN32_LEAN_AND_MEAN -DNOMINMAX")
mitkFunctionCheckCompilerFlags("/wd4005" MITK_CXX_FLAGS) # warning C4005: macro redefinition
mitkFunctionCheckCompilerFlags("/wd4231" MITK_CXX_FLAGS) # warning C4231: nonstandard extension used : 'extern' before template explicit instantiation
# the following line should be removed after fixing bug 17637
mitkFunctionCheckCompilerFlags("/wd4316" MITK_CXX_FLAGS) # warning C4316: object alignment on heap
mitkFunctionCheckCompilerFlags("/wd4180" MITK_CXX_FLAGS) # warning C4180: qualifier applied to function type has no meaning
mitkFunctionCheckCompilerFlags("/wd4251" MITK_CXX_FLAGS) # warning C4251: 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2'
endif()
if(APPLE)
set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -DGL_SILENCE_DEPRECATION") # Apple deprecated OpenGL in macOS 10.14
endif()
if(NOT MSVC_VERSION)
foreach(_flag
-Wall
-Wextra
-Wpointer-arith
-Winvalid-pch
-Wcast-align
-Wwrite-strings
-Wno-error=gnu
-Wno-error=unknown-pragmas
# The strict-overflow warning is generated by ITK template code
-Wno-error=strict-overflow
-Woverloaded-virtual
-Wstrict-null-sentinel
#-Wold-style-cast
#-Wsign-promo
- -Wno-error=deprecated-copy
+ -Wno-deprecated-copy
-Wno-array-bounds
-
+ -Wno-cast-function-type
+ -Wno-maybe-uninitialized
+ -Wno-error=stringop-overread
-fdiagnostics-show-option
)
mitkFunctionCheckCAndCXXCompilerFlags(${_flag} MITK_C_FLAGS MITK_CXX_FLAGS)
endforeach()
endif()
if(CMAKE_COMPILER_IS_GNUCXX AND NOT APPLE)
mitkFunctionCheckCompilerFlags("-Wl,--no-undefined" MITK_SHARED_LINKER_FLAGS)
mitkFunctionCheckCompilerFlags("-Wl,--as-needed" MITK_SHARED_LINKER_FLAGS)
endif()
if(CMAKE_COMPILER_IS_GNUCXX)
mitkFunctionCheckCAndCXXCompilerFlags("-fstack-protector-all" MITK_C_FLAGS MITK_CXX_FLAGS)
set(MITK_CXX_FLAGS_RELEASE "-U_FORTIFY_SOURCES -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})
foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS})
set(MITK_PACKAGE_DEPENDS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMake/PackageDepends")
if(EXISTS "${MITK_PACKAGE_DEPENDS_EXTENSION_DIR}")
list(APPEND MODULES_PACKAGE_DEPENDS_DIRS "${MITK_PACKAGE_DEPENDS_EXTENSION_DIR}")
endif()
endforeach()
if(NOT MITK_USE_SYSTEM_Boost)
set(Boost_NO_SYSTEM_PATHS 1)
endif()
set(Boost_USE_MULTITHREADED 1)
set(Boost_USE_STATIC_LIBS 0)
set(Boost_USE_STATIC_RUNTIME 0)
set(Boost_ADDITIONAL_VERSIONS 1.74 1.74.0)
# We need this later for a DCMTK workaround
set(_dcmtk_dir_orig ${DCMTK_DIR})
# This property is populated at the top half of this file
get_property(MITK_EXTERNAL_PROJECTS GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS)
foreach(ep ${MITK_EXTERNAL_PROJECTS})
get_property(_package GLOBAL PROPERTY MITK_${ep}_PACKAGE)
get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS)
if(MITK_USE_${ep} AND _package)
if(_components)
find_package(${_package} COMPONENTS ${_components} REQUIRED CONFIG)
else()
# Prefer config mode first because it finds external
# <proj>Config.cmake files pointed at by <proj>_DIR variables.
# Otherwise, existing Find<proj>.cmake files could fail.
if(DEFINED ${_package}_DIR)
#we store the information because it will be overwritten by find_package
#and would get lost for all EPs that use on Find<proj>.cmake instead of config
#files.
set(_temp_EP_${_package}_dir ${${_package}_DIR})
endif(DEFINED ${_package}_DIR)
find_package(${_package} QUIET CONFIG)
string(TOUPPER "${_package}" _package_uc)
if(NOT (${_package}_FOUND OR ${_package_uc}_FOUND))
if(DEFINED _temp_EP_${_package}_dir)
set(${_package}_DIR ${_temp_EP_${_package}_dir} CACHE PATH "externaly set dir of the package ${_package}" FORCE)
endif(DEFINED _temp_EP_${_package}_dir)
find_package(${_package} REQUIRED)
endif()
endif()
endif()
endforeach()
# Ensure that the MITK CMake module path comes first
set(CMAKE_MODULE_PATH
${MITK_CMAKE_DIR}
${CMAKE_MODULE_PATH}
)
if(MITK_USE_DCMTK)
if(${_dcmtk_dir_orig} MATCHES "${MITK_EXTERNAL_PROJECT_PREFIX}.*")
# Help our FindDCMTK.cmake script find our super-build DCMTK
set(DCMTK_DIR ${MITK_EXTERNAL_PROJECT_PREFIX})
else()
# Use the original value
set(DCMTK_DIR ${_dcmtk_dir_orig})
endif()
endif()
if(MITK_USE_DCMQI)
# Due to the preferred CONFIG mode in find_package calls above,
# the DCMQIConfig.cmake file is read, which does not provide useful
# package information. We explictly need MODULE mode to find DCMQI.
# Help our FindDCMQI.cmake script find our super-build DCMQI
set(DCMQI_DIR ${MITK_EXTERNAL_PROJECT_PREFIX})
find_package(DCMQI REQUIRED)
endif()
if(MITK_USE_OpenIGTLink)
link_directories(${OpenIGTLink_LIBRARY_DIRS})
endif()
if(MITK_USE_OpenCL)
find_package(OpenCL REQUIRED)
endif()
if(MITK_USE_OpenMP)
find_package(OpenMP REQUIRED COMPONENTS CXX)
else()
find_package(OpenMP QUIET COMPONENTS CXX)
if(OpenMP_FOUND)
set(MITK_USE_OpenMP ON CACHE BOOL "" FORCE)
elseif(APPLE AND OpenMP_libomp_LIBRARY AND NOT OpenMP_CXX_LIB_NAMES)
set(OpenMP_CXX_LIB_NAMES libomp CACHE STRING "" FORCE)
get_filename_component(openmp_lib_dir "${OpenMP_libomp_LIBRARY}" DIRECTORY)
set(openmp_include_dir "${openmp_lib_dir}/../include")
if(EXISTS "${openmp_include_dir}")
get_filename_component(openmp_include_dir "${openmp_include_dir}" REALPATH)
set(OpenMP_CXX_FLAGS "-Xpreprocessor -fopenmp -I${openmp_include_dir}" CACHE STRING "" FORCE)
find_package(OpenMP QUIET COMPONENTS CXX)
if(OpenMP_FOUND)
set(MITK_USE_OpenMP ON CACHE BOOL "" FORCE)
endif()
endif()
endif()
endif()
# Qt support
if(MITK_USE_Qt5)
find_package(Qt5Core ${MITK_QT5_MINIMUM_VERSION} REQUIRED) # at least Core required
get_target_property(_qmake_exec Qt5::qmake LOCATION)
execute_process(COMMAND ${_qmake_exec} -query QT_INSTALL_BINS
RESULT_VARIABLE _result
OUTPUT_VARIABLE QT_BINARY_DIR
ERROR_VARIABLE _error
)
string(STRIP "${QT_BINARY_DIR}" QT_BINARY_DIR)
if(_result OR NOT EXISTS "${QT_BINARY_DIR}")
message(FATAL_ERROR "Could not determine Qt binary directory: ${_result} ${QT_BINARY_DIR} ${_error}")
endif()
find_program(QT_HELPGENERATOR_EXECUTABLE
NAMES qhelpgenerator qhelpgenerator-qt5 qhelpgenerator5
PATHS ${QT_BINARY_DIR}
NO_DEFAULT_PATH
)
find_program(QT_COLLECTIONGENERATOR_EXECUTABLE
NAMES qcollectiongenerator qcollectiongenerator-qt5 qcollectiongenerator5
PATHS ${QT_BINARY_DIR}
NO_DEFAULT_PATH
)
find_program(QT_ASSISTANT_EXECUTABLE
NAMES assistant assistant-qt5 assistant5
PATHS ${QT_BINARY_DIR}
NO_DEFAULT_PATH
)
find_program(QT_XMLPATTERNS_EXECUTABLE
NAMES xmlpatterns
PATHS ${QT_BINARY_DIR}
NO_DEFAULT_PATH
)
mark_as_advanced(QT_HELPGENERATOR_EXECUTABLE
QT_COLLECTIONGENERATOR_EXECUTABLE
QT_ASSISTANT_EXECUTABLE
QT_XMLPATTERNS_EXECUTABLE
)
if(MITK_USE_BLUEBERRY)
option(BLUEBERRY_USE_QT_HELP "Enable support for integrating plugin documentation into Qt Help" ${DOXYGEN_FOUND})
mark_as_advanced(BLUEBERRY_USE_QT_HELP)
# Sanity checks for in-application BlueBerry plug-in help generation
if(BLUEBERRY_USE_QT_HELP)
set(_force_blueberry_use_qt_help_to_off 0)
if(NOT DOXYGEN_FOUND)
message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen was not found.")
set(_force_blueberry_use_qt_help_to_off 1)
endif()
if(DOXYGEN_FOUND AND DOXYGEN_VERSION VERSION_LESS 1.8.7)
message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen version 1.8.7 or newer not found.")
set(_force_blueberry_use_qt_help_to_off 1)
endif()
if(NOT QT_HELPGENERATOR_EXECUTABLE)
message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because QT_HELPGENERATOR_EXECUTABLE is empty.")
set(_force_blueberry_use_qt_help_to_off 1)
endif()
if(NOT MITK_USE_Qt5)
message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because MITK_USE_Qt5 is OFF.")
set(_force_blueberry_use_qt_help_to_off 1)
endif()
if(NOT QT_XMLPATTERNS_EXECUTABLE)
message("You have enabled Qt Help support, but QT_XMLPATTERNS_EXECUTABLE is empty")
set(_force_blueberry_use_qt_help_to_off 1)
endif()
if(_force_blueberry_use_qt_help_to_off)
set(BLUEBERRY_USE_QT_HELP OFF CACHE BOOL "Enable support for integrating plugin documentation into Qt Help" FORCE)
endif()
endif()
if(BLUEBERRY_QT_HELP_REQUIRED AND NOT BLUEBERRY_USE_QT_HELP)
message(FATAL_ERROR "BLUEBERRY_USE_QT_HELP is required to be set to ON")
endif()
endif()
endif()
#-----------------------------------------------------------------------------
# Testing
#-----------------------------------------------------------------------------
if(BUILD_TESTING)
#[[ See T27701
# Initial cache for ProjectTemplate and PluginGenerator tests
configure_file(
CMake/mitkTestInitialCache.txt.in
${MITK_BINARY_DIR}/mitkTestInitialCache.txt
@ONLY
)]]
# Configuration for the CMake-generated test driver
set(CMAKE_TESTDRIVER_EXTRA_INCLUDES "#include <stdexcept>")
set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN "
try
{")
set(CMAKE_TESTDRIVER_AFTER_TESTMAIN "
}
catch (const std::exception& e)
{
fprintf(stderr, \"%s\\n\", e.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 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()
#-----------------------------------------------------------------------------
# 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 subdirectories
#-----------------------------------------------------------------------------
add_subdirectory(Utilities)
add_subdirectory(Modules)
include("${CMAKE_CURRENT_SOURCE_DIR}/Modules/ModuleList.cmake")
mitkFunctionWhitelistModules(MITK MITK_MODULES)
set(MITK_ROOT_FOLDER_BACKUP "${MITK_ROOT_FOLDER}")
foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS})
get_filename_component(MITK_ROOT_FOLDER "${MITK_EXTENSION_DIR}" NAME)
set(MITK_MODULES_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Modules")
if(EXISTS "${MITK_MODULES_EXTENSION_DIR}/ModuleList.cmake")
set(MITK_MODULES "")
include("${MITK_MODULES_EXTENSION_DIR}/ModuleList.cmake")
foreach(mitk_module ${MITK_MODULES})
add_subdirectory("${MITK_MODULES_EXTENSION_DIR}/${mitk_module}" "Modules/${mitk_module}")
endforeach()
endif()
set(MITK_MODULE_NAME_PREFIX ${MITK_DEFAULT_MODULE_NAME_PREFIX})
endforeach()
set(MITK_ROOT_FOLDER "${MITK_ROOT_FOLDER_BACKUP}")
add_subdirectory(Wrapping)
set(MITK_DOXYGEN_OUTPUT_DIR "${PROJECT_BINARY_DIR}/Documentation/Doxygen" CACHE PATH
"Output directory for doxygen generated documentation.")
if(MITK_USE_BLUEBERRY)
include("${CMAKE_CURRENT_SOURCE_DIR}/Plugins/PluginList.cmake")
mitkFunctionWhitelistPlugins(MITK MITK_PLUGINS)
set(mitk_plugins_fullpath "")
foreach(mitk_plugin ${MITK_PLUGINS})
list(APPEND mitk_plugins_fullpath Plugins/${mitk_plugin})
endforeach()
set(MITK_PLUGIN_REGEX_LIST "")
foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS})
set(MITK_PLUGINS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Plugins")
if(EXISTS "${MITK_PLUGINS_EXTENSION_DIR}/PluginList.cmake")
set(MITK_PLUGINS "")
include("${MITK_PLUGINS_EXTENSION_DIR}/PluginList.cmake")
foreach(mitk_plugin ${MITK_PLUGINS})
list(APPEND mitk_plugins_fullpath "${MITK_PLUGINS_EXTENSION_DIR}/${mitk_plugin}")
endforeach()
endif()
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()
if(MITK_BUILD_EXAMPLES)
include("${CMAKE_CURRENT_SOURCE_DIR}/Examples/Plugins/PluginList.cmake")
set(mitk_example_plugins_fullpath )
foreach(mitk_example_plugin ${MITK_EXAMPLE_PLUGINS})
list(APPEND mitk_example_plugins_fullpath Examples/Plugins/${mitk_example_plugin})
list(APPEND mitk_plugins_fullpath Examples/Plugins/${mitk_example_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 MITK_PLUGIN_REGEX_LIST OUTPUT_VARIABLE ${varname})
endmacro()
# Get infos about application directories and build options
set(mitk_apps_fullpath "")
foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS})
set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications")
if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake")
set(MITK_APPS "")
include("${MITK_APPLICATIONS_EXTENSION_DIR}/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 0 directory_name)
list(GET target_info_list 1 option_name)
if(${option_name})
list(APPEND mitk_apps_fullpath "${MITK_APPLICATIONS_EXTENSION_DIR}/${directory_name}^^${option_name}")
endif()
endforeach()
endif()
endforeach()
if (mitk_plugins_fullpath)
ctkMacroSetupPlugins(${mitk_plugins_fullpath}
BUILD_OPTION_PREFIX MITK_BUILD_
APPS ${mitk_apps_fullpath}
BUILD_ALL ${MITK_BUILD_ALL_PLUGINS}
COMPACT_OPTIONS)
endif()
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()
endif()
#-----------------------------------------------------------------------------
# Documentation
#-----------------------------------------------------------------------------
if(DOXYGEN_FOUND)
add_subdirectory(Documentation)
endif()
#-----------------------------------------------------------------------------
# Installation
#-----------------------------------------------------------------------------
# set MITK cpack variables
# These are the default variables, which can be overwritten ( see below )
include(mitkSetupCPack)
set(use_default_config ON)
set(ALL_MITK_APPS "")
set(activated_apps_no 0)
foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS})
set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications")
if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake")
set(MITK_APPS "")
include("${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake")
foreach(mitk_app ${MITK_APPS})
string(REPLACE "^^" "\\;" target_info ${mitk_app})
set(target_info_list ${target_info})
list(GET target_info_list 0 directory_name)
list(GET target_info_list 1 option_name)
list(GET target_info_list 2 executable_name)
list(APPEND ALL_MITK_APPS "${MITK_EXTENSION_DIR}/Applications/${directory_name}^^${option_name}^^${executable_name}")
if(${option_name} OR MITK_BUILD_ALL_APPS)
MATH(EXPR activated_apps_no "${activated_apps_no} + 1")
endif()
endforeach()
endif()
endforeach()
list(LENGTH ALL_MITK_APPS app_count)
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 ${ALL_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)
list(GET target_info_list 2 executable_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 "${target_dir}/CPackOptions.cmake")
include("${target_dir}/CPackOptions.cmake")
endif()
if(EXISTS "${target_dir}/CPackConfig.cmake.in")
set(CPACK_PROJECT_CONFIG_FILE "${target_dir}/CPackConfig.cmake")
configure_file(${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 "${executable_name}")
endif()
endforeach()
# 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
#-----------------------------------------------------------------------------
# ---------------- Export targets -----------------
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()
get_target_property(deprecated_module ${target_to_export} MITK_MODULE_DEPRECATED_SINCE)
if(deprecated_module)
set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES}
set_target_properties(${target_to_export} PROPERTIES MITK_MODULE_DEPRECATED_SINCE \"${deprecated_module}\")")
endif()
endforeach()
# ---------------- External projects -----------------
get_property(MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS_CONFIG GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS)
set(MITK_CONFIG_EXTERNAL_PROJECTS )
#string(REPLACE "^^" ";" _mitk_external_projects ${MITK_EXTERNAL_PROJECTS})
foreach(ep ${MITK_EXTERNAL_PROJECTS})
get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS)
set(MITK_CONFIG_EXTERNAL_PROJECTS "${MITK_CONFIG_EXTERNAL_PROJECTS}
set(MITK_USE_${ep} ${MITK_USE_${ep}})
set(MITK_${ep}_DIR \"${${ep}_DIR}\")
set(MITK_${ep}_COMPONENTS ${_components})
")
endforeach()
foreach(ep ${MITK_EXTERNAL_PROJECTS})
get_property(_package GLOBAL PROPERTY MITK_${ep}_PACKAGE)
get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS)
if(_components)
set(_components_arg COMPONENTS \${_components})
else()
set(_components_arg)
endif()
if(_package)
set(MITK_CONFIG_EXTERNAL_PROJECTS "${MITK_CONFIG_EXTERNAL_PROJECTS}
if(MITK_USE_${ep})
set(${ep}_DIR \${MITK_${ep}_DIR})
if(MITK_${ep}_COMPONENTS)
mitkMacroFindDependency(${_package} COMPONENTS \${MITK_${ep}_COMPONENTS})
else()
mitkMacroFindDependency(${_package})
endif()
endif()")
endif()
endforeach()
# ---------------- Tools -----------------
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)
# ---------------- Configure files -----------------
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)
configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h)
configure_file(MITKConfig.cmake.in ${MITK_BINARY_DIR}/MITKConfig.cmake @ONLY)
write_basic_config_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
VERSION ${MITK_VERSION_STRING} COMPATIBILITY AnyNewerVersion)
#-----------------------------------------------------------------------------
# MITK Applications
#-----------------------------------------------------------------------------
# This must come after MITKConfig.h was generated, since applications
# might do a find_package(MITK REQUIRED).
add_subdirectory(Applications)
if(MSVC AND TARGET MitkWorkbench)
set_directory_properties(PROPERTIES VS_STARTUP_PROJECT MitkWorkbench)
endif()
foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS})
set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications")
if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/CMakeLists.txt")
add_subdirectory("${MITK_APPLICATIONS_EXTENSION_DIR}" "Applications")
endif()
endforeach()
#-----------------------------------------------------------------------------
# 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()
#-----------------------------------------------------------------------------
# Print configuration summary
#-----------------------------------------------------------------------------
message("\n\n")
feature_summary(
DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------"
WHAT ALL
)
diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/BasicDataTypes.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/BasicDataTypes.dox
index d35e6a6d97..216c7b0904 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Concepts/BasicDataTypes.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/BasicDataTypes.dox
@@ -1,60 +1,60 @@
/**
\page BasicDataTypesPage Numeric MITK data types and their usage.
-This page describes how to use very foundational data-tyes in MITK like mitk::Vector, mitk::Point and mitk::Matrix and
+This page describes how to use very foundational data-tyes in MITK like mitk::Vector, mitk::Point and mitk::Matrix and
how they can interact.
\tableofcontents
\section Structure Structure
-The previously known, monolythic structure of putting every basic type into mitkVector.h has been broken and
+The previously known, monolythic structure of putting every basic type into mitkVector.h has been broken and
mitkVector.h has been split up into several, more atomic files, namely:
-# mitkNumericConstants.h : contains basic constants like mitk::ScalarType or mitk::eps
-# mitkArray.h : copy itk::FixedArrays (like itk::Point and itk::Vector) from and to types which implement the [] operator (array-types), like e.g. Plain Old Datatypes (POD) or the opencv vector
-# mitkPoint.h : the mitk::Point class. This is basically the itk::Point with the added ToArray and Fill members to conveniently copy from and to array-types. In MITK, a point is considered a fixed geometric location and thus cannot be summed or multiplied.
--# mitkVector.h : the mitk::Vector class. This is an itk::Vector, but with the possiblity to implicitly convert to vnl_vector and vnl_vector_fixed. In MITK, vectors denote directions and can be summed or multiplied with scalars.
+-# mitkVector.h : the mitk::Vector class. This is an itk::Vector, but with the possiblity to implicitly convert to vnl_vector and vnl_vector_fixed. In MITK, vectors denote directions and can be summed or multiplied with scalars.
-# mitkMatrix.h : the mitk::Matrix class. This is an itk::Matrix with the added ToArray and Fill members to conveniently copy from and to array-types.
--# mitkQuaternion.h : a typedef to vnl_quaternion.
--# mitkAffineTransform3D.h : a typedef to itk::AffineGeometryFrame<ScalarType, 3>
+-# mitkQuaternion.h : a typedef to vnl_quaternion.
+-# mitkAffineTransform3D.h : a typedef to itk::ScalableAffineTransform<ScalarType, 3>
-# mitkNumericTypes.h : this file includes all of the above as a convenience header
-The Equal methods to compare Points, Vectors, Matrices, ... have been moved into the respective files.
+The Equal methods to compare Points, Vectors, Matrices, ... have been moved into the respective files.
E.g., if you want to compare two vectors simply use the Equal method provided by mitkVector.h.
\section Conversion Conversion between the data-types
If you want to convert a mitk::Vector from or to a vnl_vector or a vnl_vector_fixed, simply write
\code
mitkVector3D = vnlVector3D;
vnlVector3D_2 = mitkVector3D;
\endcode
Unfortunately this mechanism couldn't be implemented to every type of conversion. But in any case, the ToArray and FillVector/FillPoint/FillMatrix member
functions can be used to convert to array-types. E.g.,
\code
cv::Vec3d cvVec3D;
-
+
mitkVector3D.ToArray(cvVec3D);
mitkVector3D_2.FillVector(cvVec3D);
\endcode
No implicit conversion from mitk::Point to mitk::Vector was implemented as this would break with itk's
concept of separating points and vectors. If you want to convert, use:
\code
mitkVector3D = mitkPoint3D.GetVectorFromOrigin();
- mitkPoint3D_2 = mitkVector3D;
+ mitkPoint3D_2 = mitkVector3D;
\endcode
more examples of how to convert between data types can be found in the tests:
-# mitkArrayTypeConversionTest.cpp
-# mitkPointTypeConversionTest.cpp
-# mitkVectorTypeConversionTest.cpp
-# mitkMatrixTypeConversionTest.cpp
-*/
\ No newline at end of file
+*/
diff --git a/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/colourimageprocessing/mitkColourImageProcessor.cpp b/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/colourimageprocessing/mitkColourImageProcessor.cpp
index cebd6068d5..917a2725df 100644
--- a/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/colourimageprocessing/mitkColourImageProcessor.cpp
+++ b/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/colourimageprocessing/mitkColourImageProcessor.cpp
@@ -1,991 +1,991 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkColourImageProcessor.h"
#include <itkImage.h>
#include <mitkImage.h>
#include <mitkImageCast.h>
#include <mitkImageReadAccessor.h>
namespace mitk
{
mitkColourImageProcessor::mitkColourImageProcessor() {}
mitkColourImageProcessor::~mitkColourImageProcessor() {}
template <class T>
class ScalarToRGBAConverter
{
const T *dataPtr;
unsigned char *tmpPtr;
int sizeX;
int sizeY;
int sizeZ;
int sizeXY;
int sizeXm1;
int sizeYm1;
int sizeZm1;
mitk::TransferFunction::Pointer tf;
public:
ScalarToRGBAConverter(const T *_dataPtr,
unsigned char *_tmpPtr,
int _sizeX,
int _sizeY,
int _sizeZ,
mitk::TransferFunction::Pointer _tf)
{
dataPtr = _dataPtr;
tmpPtr = _tmpPtr;
sizeX = _sizeX;
sizeY = _sizeY;
sizeZ = _sizeZ;
sizeXY = sizeX * sizeY;
sizeXm1 = sizeX - 1;
sizeYm1 = sizeY - 1;
sizeZm1 = sizeZ - 1;
tf = _tf;
}
inline float sample(int x, int y, int z) { return float(dataPtr[x + y * sizeX + z * sizeXY]); }
inline int clamp(int x)
{
if (x < 0)
x = 0;
else if (x > 255)
x = 255;
return x;
}
inline void write(int x, int y, int z, float grayValue, float gx, float gy, float gz)
{
/*
gx /= aspect[0];
gy /= aspect[1];
gz /= aspect[2];
*/
// Compute the gradient magnitude
float t = sqrtf(gx * gx + gy * gy + gz * gz);
int doff = x + y * sizeX + z * sizeXY;
vtkPiecewiseFunction *opacityTransferFunction = tf->GetScalarOpacityFunction();
vtkPiecewiseFunction *gradientTransferFunction = tf->GetGradientOpacityFunction();
vtkColorTransferFunction *colorTransferFunction = tf->GetColorTransferFunction();
double rgb[3];
colorTransferFunction->GetColor(double(grayValue), rgb);
double opacity = opacityTransferFunction->GetValue(double(grayValue));
opacity *= gradientTransferFunction->GetValue(double(0.5f * t));
tmpPtr[doff * 4 + 0] = int(rgb[0] * 255 + 0.5);
tmpPtr[doff * 4 + 1] = int(rgb[1] * 255 + 0.5);
tmpPtr[doff * 4 + 2] = int(rgb[2] * 255 + 0.5);
tmpPtr[doff * 4 + 3] = int(opacity * 255 + 0.5);
}
inline void compute(int x, int y, int z)
{
float grayValue = sample(x, y, z);
float gx, gy, gz;
gx = sample(x + 1, y, z) - sample(x - 1, y, z);
gy = sample(x, y + 1, z) - sample(x, y - 1, z);
gz = sample(x, y, z + 1) - sample(x, y, z - 1);
write(x, y, z, grayValue, gx, gy, gz);
}
inline void computeClamp(int x, int y, int z)
{
float grayValue = sample(x, y, z);
float gx, gy, gz;
if (x == 0)
gx = 2.0f * (sample(x + 1, y, z) - grayValue);
else if (x == sizeXm1)
gx = 2.0f * (grayValue - sample(x - 1, y, z));
else
gx = sample(x + 1, y, z) - sample(x - 1, y, z);
if (y == 0)
gy = 2.0f * (sample(x, y + 1, z) - grayValue);
else if (y == sizeYm1)
gy = 2.0f * (grayValue - sample(x, y - 1, z));
else
gy = sample(x, y + 1, z) - sample(x, y - 1, z);
if (z == 0)
gz = 2.0f * (sample(x, y, z + 1) - grayValue);
else if (z == sizeZm1)
gz = 2.0f * (grayValue - sample(x, y, z - 1));
else
gz = sample(x, y, z + 1) - sample(x, y, z - 1);
write(x, y, z, grayValue, gx, gy, gz);
}
inline void compute1D(int y, int z)
{
int x;
x = 0;
computeClamp(x, y, z);
x++;
while (x < sizeX - 1)
{
compute(x, y, z);
x++;
}
if (x < sizeX)
{
computeClamp(x, y, z);
x++;
}
}
inline void computeClamp1D(int y, int z)
{
int x;
x = 0;
while (x < sizeX)
{
computeClamp(x, y, z);
x++;
}
}
inline void computeClamp2D(int z)
{
int y;
y = 0;
while (y < sizeY)
{
computeClamp1D(y, z);
y++;
}
}
inline void compute2D(int z)
{
int y;
y = 0;
computeClamp1D(y, z);
y++;
while (y < sizeY - 1)
{
compute1D(y, z);
y++;
}
if (y < sizeY)
{
computeClamp1D(y, z);
y++;
}
}
inline void fillSlices()
{
int z;
for (z = 0; z < sizeZ; z++)
{
if (z == 0 || z == sizeZ - 1)
computeClamp2D(z);
else
compute2D(z);
}
}
};
template <class TType>
mitk::Image::Pointer mitkColourImageProcessor::ScalarToRGBA(itk::Image<TType, 3> *input,
mitk::TransferFunction::Pointer tf)
{
const TType *inputData = input->GetBufferPointer();
typename itk::Image<TType, 3>::SizeType ioSize = input->GetLargestPossibleRegion().GetSize();
MITK_INFO << "size input image: " << ioSize[0] << ", " << ioSize[1] << ", " << ioSize[2];
MITK_INFO << "size voxel: " << ioSize[0] * ioSize[1] * ioSize[2];
int voxel = ioSize[0] * ioSize[1] * ioSize[2];
auto RGBABuffer = new unsigned char[4 * voxel];
// Convert volume
{
ScalarToRGBAConverter<TType> strc(inputData, RGBABuffer, ioSize[0], ioSize[1], ioSize[2], tf);
strc.fillSlices();
}
// Create MITK Image out of the raw data
{
mitk::Image::Pointer image = mitk::Image::New();
unsigned int dimensions[3];
dimensions[0] = ioSize[0];
dimensions[1] = ioSize[1];
dimensions[2] = ioSize[2];
mitk::PixelType pixelType(MakePixelType<RGBAImage>());
image->Initialize(pixelType, 3, dimensions);
image->SetImportChannel(RGBABuffer, 0, Image::ManageMemory);
return image;
}
}
mitk::Image::Pointer mitkColourImageProcessor::convertToRGBAImage(mitk::Image::Pointer mitkInput,
mitk::TransferFunction::Pointer tf)
{
MITK_INFO << "convertToRGBAImage";
mitk::Image::Pointer mitkResult = mitk::Image::New();
- if (mitkInput->GetPixelType().GetComponentType() == itk::ImageIOBase::CHAR)
+ if (mitkInput->GetPixelType().GetComponentType() == itk::IOComponentEnum::CHAR)
{
// cast to itkImage
itk::Image<unsigned char, 3>::Pointer itkInput;
mitk::CastToItkImage(mitkInput, itkInput);
mitkResult = ScalarToRGBA<unsigned char>(itkInput, tf);
}
- else if (mitkInput->GetPixelType().GetComponentType() == itk::ImageIOBase::SHORT)
+ else if (mitkInput->GetPixelType().GetComponentType() == itk::IOComponentEnum::SHORT)
{
// cast to itkImage
itk::Image<short, 3>::Pointer itkInput;
mitk::CastToItkImage(mitkInput, itkInput);
mitkResult = ScalarToRGBA<short>(itkInput, tf);
}
else
{
MITK_ERROR << "unsupported pixel type";
return nullptr;
}
mitkResult->SetClonedGeometry(mitkInput->GetGeometry());
return mitkResult;
}
template <class T, class B>
class ScalarBinaryToRGBAConverter
{
const T *dataPtr;
const B *data2Ptr;
unsigned char *tmpPtr;
int sizeX;
int sizeY;
int sizeZ;
int sizeXY;
int sizeXm1;
int sizeYm1;
int sizeZm1;
mitk::TransferFunction::Pointer tf;
public:
ScalarBinaryToRGBAConverter(const T *_dataPtr,
const B *_data2Ptr,
unsigned char *_tmpPtr,
int _sizeX,
int _sizeY,
int _sizeZ,
mitk::TransferFunction::Pointer _tf)
{
dataPtr = _dataPtr;
data2Ptr = _data2Ptr;
tmpPtr = _tmpPtr;
sizeX = _sizeX;
sizeY = _sizeY;
sizeZ = _sizeZ;
sizeXY = sizeX * sizeY;
sizeXm1 = sizeX - 1;
sizeYm1 = sizeY - 1;
sizeZm1 = sizeZ - 1;
tf = _tf;
}
inline float sample(int x, int y, int z) { return float(dataPtr[x + y * sizeX + z * sizeXY]); }
inline bool sampleBinary(int x, int y, int z) { return data2Ptr[x + y * sizeX + z * sizeXY]; }
inline int clamp(int x)
{
if (x < 0)
x = 0;
else if (x > 255)
x = 255;
return x;
}
inline void write(int x, int y, int z, float grayValue, float gx, float gy, float gz)
{
if (sampleBinary(x, y, z))
{
/*
gx /= aspect[0];
gy /= aspect[1];
gz /= aspect[2];
*/
// Compute the gradient magnitude
float t = sqrtf(gx * gx + gy * gy + gz * gz);
int doff = x + y * sizeX + z * sizeXY;
vtkPiecewiseFunction *opacityTransferFunction = tf->GetScalarOpacityFunction();
vtkPiecewiseFunction *gradientTransferFunction = tf->GetGradientOpacityFunction();
vtkColorTransferFunction *colorTransferFunction = tf->GetColorTransferFunction();
double rgb[3];
colorTransferFunction->GetColor(double(grayValue), rgb);
double opacity = opacityTransferFunction->GetValue(double(grayValue));
opacity *= gradientTransferFunction->GetValue(double(0.5f * t));
tmpPtr[doff * 4 + 0] = int(rgb[0] * 255 + 0.5);
tmpPtr[doff * 4 + 1] = int(rgb[1] * 255 + 0.5);
tmpPtr[doff * 4 + 2] = int(rgb[2] * 255 + 0.5);
tmpPtr[doff * 4 + 3] = int(opacity * 255 + 0.5);
}
else
{
int doff = x + y * sizeX + z * sizeXY;
tmpPtr[doff * 4 + 0] = 0;
tmpPtr[doff * 4 + 1] = 0;
tmpPtr[doff * 4 + 2] = 0;
tmpPtr[doff * 4 + 3] = 0;
}
}
inline void compute(int x, int y, int z)
{
float grayValue = sample(x, y, z);
float gx, gy, gz;
gx = sample(x + 1, y, z) - sample(x - 1, y, z);
gy = sample(x, y + 1, z) - sample(x, y - 1, z);
gz = sample(x, y, z + 1) - sample(x, y, z - 1);
write(x, y, z, grayValue, gx, gy, gz);
}
inline void computeClamp(int x, int y, int z)
{
float grayValue = sample(x, y, z);
float gx, gy, gz;
if (x == 0)
gx = 2.0f * (sample(x + 1, y, z) - grayValue);
else if (x == sizeXm1)
gx = 2.0f * (grayValue - sample(x - 1, y, z));
else
gx = sample(x + 1, y, z) - sample(x - 1, y, z);
if (y == 0)
gy = 2.0f * (sample(x, y + 1, z) - grayValue);
else if (y == sizeYm1)
gy = 2.0f * (grayValue - sample(x, y - 1, z));
else
gy = sample(x, y + 1, z) - sample(x, y - 1, z);
if (z == 0)
gz = 2.0f * (sample(x, y, z + 1) - grayValue);
else if (z == sizeZm1)
gz = 2.0f * (grayValue - sample(x, y, z - 1));
else
gz = sample(x, y, z + 1) - sample(x, y, z - 1);
write(x, y, z, grayValue, gx, gy, gz);
}
inline void compute1D(int y, int z)
{
int x;
x = 0;
computeClamp(x, y, z);
x++;
while (x < sizeX - 1)
{
compute(x, y, z);
x++;
}
if (x < sizeX)
{
computeClamp(x, y, z);
x++;
}
}
inline void computeClamp1D(int y, int z)
{
int x;
x = 0;
while (x < sizeX)
{
computeClamp(x, y, z);
x++;
}
}
inline void computeClamp2D(int z)
{
int y;
y = 0;
while (y < sizeY)
{
computeClamp1D(y, z);
y++;
}
}
inline void compute2D(int z)
{
int y;
y = 0;
computeClamp1D(y, z);
y++;
while (y < sizeY - 1)
{
compute1D(y, z);
y++;
}
if (y < sizeY)
{
computeClamp1D(y, z);
y++;
}
}
inline void fillSlices()
{
int z;
for (z = 0; z < sizeZ; z++)
{
if (z == 0 || z == sizeZ - 1)
computeClamp2D(z);
else
compute2D(z);
}
}
};
template <class TType, class BType>
mitk::Image::Pointer mitkColourImageProcessor::ScalarAndBinaryToRGBA(itk::Image<TType, 3> *input,
itk::Image<BType, 3> *input2,
mitk::TransferFunction::Pointer tf)
{
const TType *inputData = input->GetBufferPointer();
const BType *input2Data = input2->GetBufferPointer();
typename itk::Image<TType, 3>::SizeType ioSize = input->GetLargestPossibleRegion().GetSize();
MITK_INFO << "size input image: " << ioSize[0] << ", " << ioSize[1] << ", " << ioSize[2];
MITK_INFO << "size voxel: " << ioSize[0] * ioSize[1] * ioSize[2];
int voxel = ioSize[0] * ioSize[1] * ioSize[2];
auto RGBABuffer = new unsigned char[4 * voxel];
// for(int i=0;i<voxel;i++)
// Convert volume
{
ScalarBinaryToRGBAConverter<TType, BType> strc(
inputData, input2Data, RGBABuffer, ioSize[0], ioSize[1], ioSize[2], tf);
strc.fillSlices();
}
// Create MITK Image out of the raw data
{
mitk::Image::Pointer image = mitk::Image::New();
unsigned int dimensions[3];
dimensions[0] = ioSize[0];
dimensions[1] = ioSize[1];
dimensions[2] = ioSize[2];
mitk::PixelType pixelType(MakePixelType<RGBAImage>());
image->Initialize(pixelType, 3, dimensions);
image->SetImportChannel(RGBABuffer, 0, Image::ManageMemory);
return image;
}
}
mitk::Image::Pointer mitkColourImageProcessor::convertWithBinaryToRGBAImage(mitk::Image::Pointer input1,
mitk::Image::Pointer input2,
mitk::TransferFunction::Pointer tf)
{
MITK_INFO << "convertWithBinaryToRGBAImage";
itk::Image<short, 3>::Pointer inputCT;
itk::Image<unsigned char, 3>::Pointer inputBinary;
- if (input1->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR &&
- input2->GetPixelType().GetComponentType() == itk::ImageIOBase::SHORT)
+ if (input1->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR &&
+ input2->GetPixelType().GetComponentType() == itk::IOComponentEnum::SHORT)
{
mitk::CastToItkImage(input1, inputBinary);
mitk::CastToItkImage(input2, inputCT);
}
- else if (input1->GetPixelType().GetComponentType() == itk::ImageIOBase::SHORT &&
- input2->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR)
+ else if (input1->GetPixelType().GetComponentType() == itk::IOComponentEnum::SHORT &&
+ input2->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR)
{
mitk::CastToItkImage(input1, inputCT);
mitk::CastToItkImage(input2, inputBinary);
}
else
{
MITK_ERROR << "unsupported pixel type";
return nullptr;
}
mitk::Image::Pointer resultImage = ScalarAndBinaryToRGBA<short, unsigned char>(inputCT, inputBinary, tf);
resultImage->SetClonedGeometry(input1->GetGeometry());
return resultImage;
}
//////////////////////////////////////////
template <class T, class B>
class ScalarBinaryColorToRGBAConverter
{
const T *dataPtr;
const B *data2Ptr;
unsigned char *tmpPtr;
int sizeX;
int sizeY;
int sizeZ;
int sizeXY;
int sizeXm1;
int sizeYm1;
int sizeZm1;
mitk::TransferFunction::Pointer tf;
int *color;
public:
ScalarBinaryColorToRGBAConverter(const T *_dataPtr,
const B *_data2Ptr,
unsigned char *_tmpPtr,
int _sizeX,
int _sizeY,
int _sizeZ,
mitk::TransferFunction::Pointer _tf,
int *_color)
{
dataPtr = _dataPtr;
data2Ptr = _data2Ptr;
tmpPtr = _tmpPtr;
sizeX = _sizeX;
sizeY = _sizeY;
sizeZ = _sizeZ;
sizeXY = sizeX * sizeY;
sizeXm1 = sizeX - 1;
sizeYm1 = sizeY - 1;
sizeZm1 = sizeZ - 1;
tf = _tf;
color = _color;
}
inline float sample(int x, int y, int z) { return float(dataPtr[x + y * sizeX + z * sizeXY]); }
inline bool sampleBinary(int x, int y, int z) { return data2Ptr[x + y * sizeX + z * sizeXY]; }
inline int clamp(int x)
{
if (x < 0)
x = 0;
else if (x > 255)
x = 255;
return x;
}
inline void write(int x, int y, int z, float grayValue, float gx, float gy, float gz)
{
if (sampleBinary(x, y, z))
{
/*
gx /= aspect[0];
gy /= aspect[1];
gz /= aspect[2];
*/
// Compute the gradient magnitude
float t = sqrtf(gx * gx + gy * gy + gz * gz);
int doff = x + y * sizeX + z * sizeXY;
vtkPiecewiseFunction *opacityTransferFunction = tf->GetScalarOpacityFunction();
vtkPiecewiseFunction *gradientTransferFunction = tf->GetGradientOpacityFunction();
vtkColorTransferFunction *colorTransferFunction = tf->GetColorTransferFunction();
double rgb[3];
colorTransferFunction->GetColor(double(grayValue), rgb);
double opacity = opacityTransferFunction->GetValue(double(grayValue));
opacity *= gradientTransferFunction->GetValue(double(0.5f * t));
tmpPtr[doff * 4 + 0] = int(rgb[0] * 255 + 0.5);
tmpPtr[doff * 4 + 1] = int(rgb[1] * 255 + 0.5);
tmpPtr[doff * 4 + 2] = int(rgb[2] * 255 + 0.5);
tmpPtr[doff * 4 + 3] = int(opacity * 255 + 0.5);
tmpPtr[doff * 4 + 0] = color[0];
tmpPtr[doff * 4 + 1] = color[1];
tmpPtr[doff * 4 + 2] = color[2];
}
else
{
int doff = x + y * sizeX + z * sizeXY;
tmpPtr[doff * 4 + 0] = 0;
tmpPtr[doff * 4 + 1] = 0;
tmpPtr[doff * 4 + 2] = 0;
tmpPtr[doff * 4 + 3] = 0;
}
}
inline void compute(int x, int y, int z)
{
float grayValue = sample(x, y, z);
float gx, gy, gz;
gx = sample(x + 1, y, z) - sample(x - 1, y, z);
gy = sample(x, y + 1, z) - sample(x, y - 1, z);
gz = sample(x, y, z + 1) - sample(x, y, z - 1);
write(x, y, z, grayValue, gx, gy, gz);
}
inline void computeClamp(int x, int y, int z)
{
float grayValue = sample(x, y, z);
float gx, gy, gz;
if (x == 0)
gx = 2.0f * (sample(x + 1, y, z) - grayValue);
else if (x == sizeXm1)
gx = 2.0f * (grayValue - sample(x - 1, y, z));
else
gx = sample(x + 1, y, z) - sample(x - 1, y, z);
if (y == 0)
gy = 2.0f * (sample(x, y + 1, z) - grayValue);
else if (y == sizeYm1)
gy = 2.0f * (grayValue - sample(x, y - 1, z));
else
gy = sample(x, y + 1, z) - sample(x, y - 1, z);
if (z == 0)
gz = 2.0f * (sample(x, y, z + 1) - grayValue);
else if (z == sizeZm1)
gz = 2.0f * (grayValue - sample(x, y, z - 1));
else
gz = sample(x, y, z + 1) - sample(x, y, z - 1);
write(x, y, z, grayValue, gx, gy, gz);
}
inline void compute1D(int y, int z)
{
int x;
x = 0;
computeClamp(x, y, z);
x++;
while (x < sizeX - 1)
{
compute(x, y, z);
x++;
}
if (x < sizeX)
{
computeClamp(x, y, z);
x++;
}
}
inline void computeClamp1D(int y, int z)
{
int x;
x = 0;
while (x < sizeX)
{
computeClamp(x, y, z);
x++;
}
}
inline void computeClamp2D(int z)
{
int y;
y = 0;
while (y < sizeY)
{
computeClamp1D(y, z);
y++;
}
}
inline void compute2D(int z)
{
int y;
y = 0;
computeClamp1D(y, z);
y++;
while (y < sizeY - 1)
{
compute1D(y, z);
y++;
}
if (y < sizeY)
{
computeClamp1D(y, z);
y++;
}
}
inline void fillSlices()
{
int z;
for (z = 0; z < sizeZ; z++)
{
if (z == 0 || z == sizeZ - 1)
computeClamp2D(z);
else
compute2D(z);
}
}
};
template <class TType, class BType>
mitk::Image::Pointer mitkColourImageProcessor::ScalarAndBinaryAndColorToRGBA(itk::Image<TType, 3> *input,
itk::Image<BType, 3> *input2,
mitk::TransferFunction::Pointer tf,
int *color)
{
const TType *inputData = input->GetBufferPointer();
const BType *input2Data = input2->GetBufferPointer();
typename itk::Image<TType, 3>::SizeType ioSize = input->GetLargestPossibleRegion().GetSize();
MITK_INFO << "size input image: " << ioSize[0] << ", " << ioSize[1] << ", " << ioSize[2];
MITK_INFO << "size voxel: " << ioSize[0] * ioSize[1] * ioSize[2];
int voxel = ioSize[0] * ioSize[1] * ioSize[2];
auto RGBABuffer = new unsigned char[4 * voxel];
// for(int i=0;i<voxel;i++)
// Convert volume
{
ScalarBinaryColorToRGBAConverter<TType, BType> strc(
inputData, input2Data, RGBABuffer, ioSize[0], ioSize[1], ioSize[2], tf, color);
strc.fillSlices();
}
// Create MITK Image out of the raw data
{
mitk::Image::Pointer image = mitk::Image::New();
unsigned int dimensions[3];
dimensions[0] = ioSize[0];
dimensions[1] = ioSize[1];
dimensions[2] = ioSize[2];
mitk::PixelType pixelType(MakePixelType<RGBAImage>());
image->Initialize(pixelType, 3, dimensions);
image->SetImportChannel(RGBABuffer, 0, Image::ManageMemory);
// image->GetGeometry()->SetSpacing(input->GetSpacing());
// image->GetGeometry()->SetOrigin(input->GetOrigin());
return image;
}
}
mitk::Image::Pointer mitkColourImageProcessor::convertWithBinaryAndColorToRGBAImage(
mitk::Image::Pointer input1, mitk::Image::Pointer input2, mitk::TransferFunction::Pointer tf, int *color)
{
MITK_INFO << "convertWithBinaryToRGBAImage";
itk::Image<short, 3>::Pointer inputCT;
itk::Image<unsigned char, 3>::Pointer inputBinary;
- if (input1->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR &&
- input2->GetPixelType().GetComponentType() == itk::ImageIOBase::SHORT)
+ if (input1->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR &&
+ input2->GetPixelType().GetComponentType() == itk::IOComponentEnum::SHORT)
{
mitk::CastToItkImage(input1, inputBinary);
mitk::CastToItkImage(input2, inputCT);
}
- else if (input1->GetPixelType().GetComponentType() == itk::ImageIOBase::SHORT &&
- input2->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR)
+ else if (input1->GetPixelType().GetComponentType() == itk::IOComponentEnum::SHORT &&
+ input2->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR)
{
mitk::CastToItkImage(input1, inputCT);
mitk::CastToItkImage(input2, inputBinary);
}
else
{
MITK_ERROR << "unsupported pixel type";
return nullptr;
}
mitk::Image::Pointer resultImage =
ScalarAndBinaryAndColorToRGBA<short, unsigned char>(inputCT, inputBinary, tf, color);
resultImage->SetClonedGeometry(input1->GetGeometry());
return resultImage;
}
mitk::Image::Pointer mitkColourImageProcessor::CombineRGBAImage(
const unsigned char *input, const unsigned char *input2, int sizeX, int sizeY, int sizeZ)
{
int voxel = sizeX * sizeY * sizeZ;
auto RGBABuffer = new unsigned char[4 * voxel];
// Convert volume
{
for (int r = 0; r < voxel; r++)
{
int rgbaInput1[4];
int rgbaInput2[4];
rgbaInput1[0] = input[r * 4 + 0];
rgbaInput1[1] = input[r * 4 + 1];
rgbaInput1[2] = input[r * 4 + 2];
rgbaInput1[3] = input[r * 4 + 3];
rgbaInput2[0] = input2[r * 4 + 0];
rgbaInput2[1] = input2[r * 4 + 1];
rgbaInput2[2] = input2[r * 4 + 2];
rgbaInput2[3] = input2[r * 4 + 3];
int result[4];
/*
float fac1 = rgbaInput1[3]/255.0f;
float fac2 = rgbaInput2[3]/255.0f;
float sum = fac1+fac2;
float cor = 1.0f;
if(sum > 1.0f)
cor = 1.0f/sum;
fac1 *= cor;
fac2 *= cor;
result[0]= clamp(int(fac1 * rgbaInput1[0] + fac2 * rgbaInput2[0] + 0.5f));
result[1]= clamp(int(fac1 * rgbaInput1[1] + fac2 * rgbaInput2[1] + 0.5f));
result[2]= clamp(int(fac1 * rgbaInput1[2] + fac2 * rgbaInput2[2] + 0.5f));
result[3]= clamp(int(fac1 * rgbaInput1[3] + fac2 * rgbaInput2[3] + 0.5f));
*/
if (rgbaInput1[3])
{
result[0] = rgbaInput1[0];
result[1] = rgbaInput1[1];
result[2] = rgbaInput1[2];
result[3] = rgbaInput1[3];
}
else
{
result[0] = rgbaInput2[0];
result[1] = rgbaInput2[1];
result[2] = rgbaInput2[2];
result[3] = rgbaInput2[3];
}
RGBABuffer[r * 4 + 0] = result[0];
RGBABuffer[r * 4 + 1] = result[1];
RGBABuffer[r * 4 + 2] = result[2];
RGBABuffer[r * 4 + 3] = result[3];
}
}
// Create MITK Image out of the raw data
{
mitk::Image::Pointer image = mitk::Image::New();
unsigned int dimensions[3];
dimensions[0] = sizeX;
dimensions[1] = sizeY;
dimensions[2] = sizeZ;
mitk::PixelType pixelType(MakePixelType<RGBAImage>());
image->Initialize(pixelType, 3, dimensions);
image->SetImportChannel(RGBABuffer, 0, Image::ManageMemory);
return image;
}
}
mitk::Image::Pointer mitkColourImageProcessor::combineRGBAImage(mitk::Image::Pointer input1,
mitk::Image::Pointer input2)
{
// Order access to a whole Image object
try
{
mitk::ImageReadAccessor img1(input1);
const unsigned char *data1 = (const unsigned char *)img1.GetData();
mitk::ImageReadAccessor img2(input2);
const unsigned char *data2 = (const unsigned char *)img2.GetData();
unsigned int *dim = input1->GetDimensions();
return CombineRGBAImage(data1, data2, dim[0], dim[1], dim[2]);
}
catch ( const mitk::Exception &e )
{
MITK_ERROR << "mitkColourImageProcessor::combineRGBAImage - No access to image data possible." << e.what();
return nullptr;
}
}
} // end namespace mitk
diff --git a/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/simpleexample/QmitkSimpleExampleView.cpp b/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/simpleexample/QmitkSimpleExampleView.cpp
index c5e378be43..90af8f2e7e 100644
--- a/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/simpleexample/QmitkSimpleExampleView.cpp
+++ b/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/simpleexample/QmitkSimpleExampleView.cpp
@@ -1,418 +1,418 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "QmitkSimpleExampleView.h"
#include <vtkImageWriter.h>
#include <vtkJPEGWriter.h>
#include <vtkPNGWriter.h>
#include <vtkRenderLargeImage.h>
#include <vtkRenderWindow.h>
-#include <vtkOpenGL.h>
+#include <vtk_glew.h>
#include "QmitkRenderWindow.h"
#include "QmitkStepperAdapter.h"
#include "QmitkFFmpegWriter.h"
#include "mitkNodePredicateNot.h"
#include "mitkNodePredicateProperty.h"
#include "mitkProperties.h"
#include <QDir>
#include <QFileDialog>
#include <QFileInfo>
#include <QMessageBox>
#include <berryPlatform.h>
const std::string QmitkSimpleExampleView::VIEW_ID = "org.mitk.views.simpleexample";
QmitkSimpleExampleView::QmitkSimpleExampleView()
: m_Controls(nullptr), m_NavigatorsInitialized(false), m_Parent(nullptr)
{
}
QmitkSimpleExampleView::~QmitkSimpleExampleView()
{
}
void QmitkSimpleExampleView::CreateQtPartControl(QWidget *parent)
{
if (!m_Controls)
{
m_Parent = parent;
// create GUI widgets
m_Controls = new Ui::QmitkSimpleExampleViewControls;
m_Controls->setupUi(parent);
this->CreateConnections();
this->RenderWindowPartActivated(this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN));
}
}
void QmitkSimpleExampleView::SetFocus()
{
m_Controls->renderWindowComboBox->setFocus();
}
void QmitkSimpleExampleView::RenderWindowPartActivated(mitk::IRenderWindowPart *renderWindowPart)
{
if (renderWindowPart == nullptr)
{
m_Parent->setEnabled(false);
return;
}
QHashIterator<QString, QmitkRenderWindow *> renderIter(renderWindowPart->GetQmitkRenderWindows());
while (renderIter.hasNext())
{
renderIter.next();
m_Controls->renderWindowComboBox->addItem(renderIter.key());
}
RenderWindowSelected(m_Controls->renderWindowComboBox->currentText());
m_TimeStepper.reset(new QmitkStepperAdapter(m_Controls->sliceNavigatorTime,
renderWindowPart->GetTimeNavigationController()->GetTime(),
"sliceNavigatorTimeFromSimpleExample"));
m_MovieStepper.reset(new QmitkStepperAdapter(m_Controls->movieNavigatorTime,
renderWindowPart->GetTimeNavigationController()->GetTime(),
"movieNavigatorTimeFromSimpleExample"));
m_Parent->setEnabled(true);
}
void QmitkSimpleExampleView::RenderWindowPartDeactivated(mitk::IRenderWindowPart * /*renderWindowPart*/)
{
m_Parent->setEnabled(false);
m_SliceStepper.reset();
m_TimeStepper.reset();
m_MovieStepper.reset();
m_Controls->renderWindowComboBox->clear();
}
void QmitkSimpleExampleView::CreateConnections()
{
if (m_Controls)
{
connect(m_Controls->renderWindowComboBox,
SIGNAL(currentIndexChanged(QString)),
this,
SLOT(RenderWindowSelected(QString)));
connect(m_Controls->stereoSelect, SIGNAL(activated(int)), this, SLOT(StereoSelectionChanged(int)));
connect(m_Controls->reInitializeNavigatorsButton, SIGNAL(clicked()), this, SLOT(InitNavigators()));
connect(m_Controls->genMovieButton, SIGNAL(clicked()), this, SLOT(GenerateMovie()));
connect(m_Controls->m_TakeScreenshotBtn, SIGNAL(clicked()), this, SLOT(OnTakeScreenshot()));
connect(m_Controls->m_TakeHighResScreenShotBtn, SIGNAL(clicked()), this, SLOT(OnTakeHighResolutionScreenshot()));
}
}
void QmitkSimpleExampleView::InitNavigators()
{
/* get all nodes that have not set "includeInBoundingBox" to false */
mitk::NodePredicateNot::Pointer pred = mitk::NodePredicateNot::New(
mitk::NodePredicateProperty::New("includeInBoundingBox", mitk::BoolProperty::New(false)));
mitk::DataStorage::SetOfObjects::ConstPointer rs = this->GetDataStorage()->GetSubset(pred);
/* calculate bounding geometry of these nodes */
auto bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(rs);
/* initialize the views to the bounding geometry */
m_NavigatorsInitialized = mitk::RenderingManager::GetInstance()->InitializeViews(bounds);
}
/**
* Returns path to the ffmpeg lib if configured in preferences.
*
* This implementation has been reused from MovieMaker view.
*
* @return The path to ffmpeg lib or empty string if not configured.
*/
QString QmitkSimpleExampleView::GetFFmpegPath() const
{
berry::IPreferences::Pointer preferences =
berry::Platform::GetPreferencesService()->GetSystemPreferences()->Node("/org.mitk.gui.qt.ext.externalprograms");
return preferences.IsNotNull() ? preferences->Get("ffmpeg", "") : "";
}
/**
* Reads pixels from specified render window.
*
* This implementation has been reused from MovieMaker view.
*
* @param renderWindow
* @param x
* @param y
* @param width
* @param height
* @return
*/
static unsigned char *ReadPixels(vtkRenderWindow *renderWindow, int x, int y, int width, int height)
{
if (renderWindow == nullptr)
return nullptr;
unsigned char *frame = new unsigned char[width * height * 3];
renderWindow->MakeCurrent();
glReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, frame);
return frame;
}
/**
* Records a movie from the selected render window with a default frame rate of 30 Hz.
*
* Parts of this implementation have been reused from MovieMaker view.
*/
void QmitkSimpleExampleView::GenerateMovie()
{
QmitkRenderWindow *movieRenderWindow = GetSelectedRenderWindow();
mitk::Stepper::Pointer stepper = movieRenderWindow->GetSliceNavigationController()->GetSlice();
QmitkFFmpegWriter *movieWriter = new QmitkFFmpegWriter(m_Parent);
const QString ffmpegPath = GetFFmpegPath();
if (ffmpegPath.isEmpty())
{
QMessageBox::information(
nullptr,
"Movie Maker",
"<p>Set path to FFmpeg<sup>1</sup> in preferences (Window -> Preferences... "
"(Ctrl+P) -> External Programs) to be able to record your movies to video files.</p>"
"<p>If you are using Linux, chances are good that FFmpeg is included in the official package "
"repositories.</p>"
"<p>[1] <a href=\"https://www.ffmpeg.org/download.html\">Download FFmpeg from ffmpeg.org</a></p>");
return;
}
movieWriter->SetFFmpegPath(GetFFmpegPath());
vtkRenderWindow *renderWindow = movieRenderWindow->renderWindow();
if (renderWindow == nullptr)
return;
const int border = 3;
const int x = border;
const int y = border;
int width = renderWindow->GetSize()[0] - border * 2;
int height = renderWindow->GetSize()[1] - border * 2;
if (width & 1)
--width;
if (height & 1)
--height;
if (width < 16 || height < 16)
return;
movieWriter->SetSize(width, height);
movieWriter->SetFramerate(30);
QString saveFileName = QFileDialog::getSaveFileName(nullptr, "Specify a filename", "", "Movie (*.mp4)");
if (saveFileName.isEmpty())
return;
if (!saveFileName.endsWith(".mp4"))
saveFileName += ".mp4";
movieWriter->SetOutputPath(saveFileName);
const unsigned int numberOfFrames = stepper->GetSteps() - stepper->GetPos();
try
{
movieWriter->Start();
for (unsigned int currentFrame = 0; currentFrame < numberOfFrames; ++currentFrame)
{
mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll();
renderWindow->MakeCurrent();
unsigned char *frame = ReadPixels(renderWindow, x, y, width, height);
movieWriter->WriteFrame(frame);
delete[] frame;
stepper->Next();
}
movieWriter->Stop();
mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll();
}
catch (const mitk::Exception &exception)
{
mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll();
QMessageBox::critical(nullptr, "Generate Movie", exception.GetDescription());
}
}
void QmitkSimpleExampleView::StereoSelectionChanged(int id)
{
/* From vtkRenderWindow.h tells us about stereo rendering:
Set/Get what type of stereo rendering to use. CrystalEyes mode uses frame-sequential capabilities available in OpenGL
to drive LCD shutter glasses and stereo projectors. RedBlue mode is a simple type of stereo for use with red-blue
glasses. Anaglyph mode is a superset of RedBlue mode, but the color output channels can be configured using the
AnaglyphColorMask and the color of the original image can be (somewhat maintained using AnaglyphColorSaturation; the
default colors for Anaglyph mode is red-cyan. Interlaced stereo mode produces a composite image where horizontal
lines alternate between left and right views. StereoLeft and StereoRight modes choose one or the other stereo view.
Dresden mode is yet another stereoscopic interleaving.
*/
auto *renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN);
vtkRenderWindow *vtkrenderwindow = renderWindowPart->GetQmitkRenderWindow("3d")->GetVtkRenderWindow();
// note: foreground vtkRenderers (at least the department logo renderer) produce errors in stereoscopic visualization.
// Therefore, we disable the logo visualization during stereo rendering.
switch (id)
{
case 0:
vtkrenderwindow->StereoRenderOff();
break;
case 1:
vtkrenderwindow->SetStereoTypeToRedBlue();
vtkrenderwindow->StereoRenderOn();
renderWindowPart->EnableDecorations(false, QStringList(mitk::IRenderWindowPart::DECORATION_LOGO));
break;
case 2:
vtkrenderwindow->SetStereoTypeToDresden();
vtkrenderwindow->StereoRenderOn();
renderWindowPart->EnableDecorations(false, QStringList(mitk::IRenderWindowPart::DECORATION_LOGO));
break;
}
mitk::BaseRenderer::GetInstance(vtkrenderwindow)->SetMapperID(mitk::BaseRenderer::Standard3D);
renderWindowPart->RequestUpdate();
}
QmitkRenderWindow *QmitkSimpleExampleView::GetSelectedRenderWindow() const
{
QString id = m_Controls->renderWindowComboBox->currentText();
if (id.isEmpty())
{
return nullptr;
}
else
{
return this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow(id);
}
}
void QmitkSimpleExampleView::OnTakeHighResolutionScreenshot()
{
QString filter;
QString fileName = QFileDialog::getSaveFileName(
nullptr, "Save screenshot to...", QDir::currentPath(), m_PNGExtension + ";;" + m_JPGExtension, &filter);
vtkRenderer *renderer = this->GetSelectedRenderWindow()->GetRenderer()->GetVtkRenderer();
if (renderer == nullptr)
return;
this->TakeScreenshot(renderer, 4, fileName, filter);
}
void QmitkSimpleExampleView::OnTakeScreenshot()
{
QString filter;
QString fileName = QFileDialog::getSaveFileName(
nullptr, "Save screenshot to...", QDir::currentPath(), m_PNGExtension + ";;" + m_JPGExtension, &filter);
QmitkRenderWindow *renWin = this->GetSelectedRenderWindow();
if (renWin == nullptr)
return;
vtkRenderer *renderer = renWin->GetRenderer()->GetVtkRenderer();
if (renderer == nullptr)
return;
this->TakeScreenshot(renderer, 1, fileName, filter);
}
void QmitkSimpleExampleView::TakeScreenshot(vtkRenderer *renderer,
unsigned int magnificationFactor,
QString fileName,
QString filter)
{
if ((renderer == nullptr) || (magnificationFactor < 1) || fileName.isEmpty())
return;
bool doubleBuffering(renderer->GetRenderWindow()->GetDoubleBuffer());
renderer->GetRenderWindow()->DoubleBufferOff();
vtkImageWriter *fileWriter = nullptr;
QFileInfo fi(fileName);
QString suffix = fi.suffix().toLower();
if (suffix.isEmpty() || (suffix != "png" && suffix != "jpg" && suffix != "jpeg"))
{
if (filter == m_PNGExtension)
{
suffix = "png";
}
else if (filter == m_JPGExtension)
{
suffix = "jpg";
}
fileName += "." + suffix;
}
if (suffix.compare("jpg", Qt::CaseInsensitive) == 0 || suffix.compare("jpeg", Qt::CaseInsensitive) == 0)
{
vtkJPEGWriter *w = vtkJPEGWriter::New();
w->SetQuality(100);
w->ProgressiveOff();
fileWriter = w;
}
else // default is png
{
fileWriter = vtkPNGWriter::New();
}
vtkRenderLargeImage *magnifier = vtkRenderLargeImage::New();
magnifier->SetInput(renderer);
magnifier->SetMagnification(magnificationFactor);
fileWriter->SetInputConnection(magnifier->GetOutputPort());
fileWriter->SetFileName(fileName.toLatin1());
// vtkRenderLargeImage has problems with different layers, therefore we have to
// temporarily deactivate all other layers.
// we set the background to white, because it is nicer than black...
double oldBackground[3];
renderer->GetBackground(oldBackground);
double white[] = {1.0, 1.0, 1.0};
renderer->SetBackground(white);
mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN);
renderWindowPart->EnableDecorations(false);
fileWriter->Write();
fileWriter->Delete();
renderWindowPart->EnableDecorations(true);
renderer->SetBackground(oldBackground);
renderer->GetRenderWindow()->SetDoubleBuffer(doubleBuffering);
}
void QmitkSimpleExampleView::RenderWindowSelected(const QString &id)
{
if (!id.isEmpty())
{
m_SliceStepper.reset(new QmitkStepperAdapter(
m_Controls->sliceNavigator,
this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow(id)->GetSliceNavigationController()->GetSlice(),
"sliceNavigatorFromSimpleExample"));
}
}
diff --git a/Examples/Plugins/org.mitk.example.gui.regiongrowing/CMakeLists.txt b/Examples/Plugins/org.mitk.example.gui.regiongrowing/CMakeLists.txt
index ee279d1db8..30b07ca95e 100644
--- a/Examples/Plugins/org.mitk.example.gui.regiongrowing/CMakeLists.txt
+++ b/Examples/Plugins/org.mitk.example.gui.regiongrowing/CMakeLists.txt
@@ -1,8 +1,9 @@
project(org_mitk_example_gui_regiongrowing)
mitk_create_plugin(
EXPORT_DIRECTIVE REGIONGROWING_EXPORT
EXPORTED_INCLUDE_SUFFIXES src
MODULE_DEPENDS MitkQtWidgetsExt
+ PACKAGE_DEPENDS ITK|RegionGrowing
NO_INSTALL
)
diff --git a/Examples/Tutorial/Step6/CMakeLists.txt b/Examples/Tutorial/Step6/CMakeLists.txt
index 8fb1bbb466..c486d97f84 100644
--- a/Examples/Tutorial/Step6/CMakeLists.txt
+++ b/Examples/Tutorial/Step6/CMakeLists.txt
@@ -1,3 +1,4 @@
mitk_create_executable(
DEPENDS MitkQtWidgetsExt
+ PACKAGE_DEPENDS ITK|RegionGrowing
)
diff --git a/Examples/Tutorial/Step7/CMakeLists.txt b/Examples/Tutorial/Step7/CMakeLists.txt
index 13c7b070a6..f3a9489e05 100644
--- a/Examples/Tutorial/Step7/CMakeLists.txt
+++ b/Examples/Tutorial/Step7/CMakeLists.txt
@@ -1,5 +1,6 @@
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../Step6)
mitk_create_executable(
DEPENDS MitkQtWidgetsExt
+ PACKAGE_DEPENDS ITK|RegionGrowing
)
diff --git a/Examples/Tutorial/Step8/CMakeLists.txt b/Examples/Tutorial/Step8/CMakeLists.txt
index a090d34d9e..aa07199000 100644
--- a/Examples/Tutorial/Step8/CMakeLists.txt
+++ b/Examples/Tutorial/Step8/CMakeLists.txt
@@ -1,8 +1,9 @@
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/../Step6
${CMAKE_CURRENT_SOURCE_DIR}/../Step7
)
mitk_create_executable(
DEPENDS MitkQtWidgetsExt
+ PACKAGE_DEPENDS ITK|RegionGrowing
)
diff --git a/Modules/AlgorithmsExt/include/mitkCropTimestepsImageFilter.h b/Modules/AlgorithmsExt/include/mitkCropTimestepsImageFilter.h
index 6390dfb533..72e6d9ca55 100644
--- a/Modules/AlgorithmsExt/include/mitkCropTimestepsImageFilter.h
+++ b/Modules/AlgorithmsExt/include/mitkCropTimestepsImageFilter.h
@@ -1,80 +1,80 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkCropTimestepsImageFilter_h
#define mitkCropTimestepsImageFilter_h
#include "MitkAlgorithmsExtExports.h"
#include <mitkSubImageSelector.h>
namespace mitk
{
/** \brief Crops timesteps at 2D+t and 3D+t images
* \details The filter is able to crop timesteps in front and/or at the end.
* Internally, a new image is created with the remaining volumes. The geometries and properties of
* the input image are transferred to the output image.
*/
class MITKALGORITHMSEXT_EXPORT CropTimestepsImageFilter : public SubImageSelector
{
public:
mitkClassMacro(CropTimestepsImageFilter, SubImageSelector);
itkFactorylessNewMacro(Self);
/*!
* \brief Sets the input image
* \pre the image has 4 Dimensions
* \pre the image has >1 timestep
* \pre the image is valid (valid geometry and volume)
*/
void SetInput(const InputImageType* image) override;
void SetInput(unsigned int index, const InputImageType* image) override;
/*!
* \brief The last timestep to retain
* \details Set to the maximum timestep of the input as default
* \note if the last timestep is larger than the maximum timestep of the input,
* it is corrected to the maximum timestep.
* \exception if (LowerBoundaryTimestep > UpperBoundaryTimestep)
*/
itkSetMacro(UpperBoundaryTimestep, unsigned int);
itkGetConstMacro(UpperBoundaryTimestep, unsigned int);
/*!
* \brief The first timestep to retain
* \details Set to 0 as default
* \exception if (LowerBoundaryTimestep > UpperBoundaryTimestep)
*/
itkSetMacro(LowerBoundaryTimestep, unsigned int);
itkGetConstMacro(LowerBoundaryTimestep, unsigned int);
private:
using Superclass::SetInput;
CropTimestepsImageFilter() = default;
~CropTimestepsImageFilter() override = default;
void GenerateData() override;
- void VerifyInputInformation() override;
+ void VerifyInputInformation() const override;
void VerifyInputImage(const mitk::Image* inputImage) const;
void GenerateOutputInformation() override;
mitk::SlicedData::RegionType ComputeDesiredRegion() const;
mitk::TimeGeometry::Pointer AdaptTimeGeometry(mitk::TimeGeometry::ConstPointer sourceGeometry, unsigned int startTimestep, unsigned int endTimestep) const;
unsigned int m_UpperBoundaryTimestep = std::numeric_limits<unsigned int>::max();
unsigned int m_LowerBoundaryTimestep = 0;
mitk::SlicedData::RegionType m_DesiredRegion;
};
}
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkNonBlockingAlgorithm.h b/Modules/AlgorithmsExt/include/mitkNonBlockingAlgorithm.h
index 9ea599e365..0af199cbb1 100644
--- a/Modules/AlgorithmsExt/include/mitkNonBlockingAlgorithm.h
+++ b/Modules/AlgorithmsExt/include/mitkNonBlockingAlgorithm.h
@@ -1,251 +1,249 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITK_NON_BLOCKING_ALGORITHM_H_INCLUDED_DFARdfWN1tr
#define MITK_NON_BLOCKING_ALGORITHM_H_INCLUDED_DFARdfWN1tr
#include "MitkAlgorithmsExtExports.h"
-#include <itkFastMutexLock.h>
#include <itkImage.h>
#include <itkMacro.h>
-#include <itkMultiThreader.h>
#include <itkObjectFactory.h>
#include "mitkCommon.h"
#include "mitkDataStorage.h"
#include "mitkProperties.h"
#include "mitkPropertyList.h"
#include "mitkSmartPointerProperty.h"
#include "mitkWeakPointer.h"
#include "mitkImage.h"
#include "mitkSurface.h"
+#include <mutex>
#include <stdexcept>
#include <string>
/// from itkNewMacro(), additionally calls Initialize(), because this couldn't be done from the constructor of
/// NonBlockingAlgorithm
/// (you can't call virtual functions from the constructor of the superclass)
#define mitkAlgorithmNewMacro(classname) \
\
static Pointer \
New(void) \
{ \
classname *rawPtr = new classname(); \
Pointer smartPtr = rawPtr; \
rawPtr->UnRegister(); \
rawPtr->Initialize(); \
return smartPtr; \
\
} \
\
virtual::itk::LightObject::Pointer \
CreateAnother(void) const override \
\
{ \
Pointer smartPtr = classname::New(); \
::itk::LightObject::Pointer lightPtr = smartPtr.GetPointer(); \
smartPtr->Initialize(this); \
return lightPtr; \
\
}
namespace mitk
{
/*!
Invokes ResultsAvailable with each new result
<b>done</b> centralize use of itk::MultiThreader in this class
@todo do the property-handling in this class
@todo process "incoming" events in this class
@todo sollen segmentierungs-dinger von mitk::ImageSource erben? Ivo fragen, wie das mit AllocateOutputs, etc.
gehen soll
eine ImageSourceAlgorithm koennte dann die noetigen Methoden wie GenerateData(), GetOutput() ueberschreiben,
so
dass von dort aus die Methoden von NonBlockingAlgorithm aufgerufen werden.
Erben v.a. um die Output-Sachen zu uebernehmen, die Anpassungen das einfuehren einer Zwischenklasse, um die
Interaces zu verheiraten.
*/
class MITKALGORITHMSEXT_EXPORT NonBlockingAlgorithm : public itk::Object
{
public:
// for threading
class MITKALGORITHMSEXT_EXPORT ThreadParameters
{
public:
itk::SmartPointer<NonBlockingAlgorithm> m_Algorithm;
};
mitkClassMacroItkParent(NonBlockingAlgorithm, itk::Object);
void SetDataStorage(DataStorage &storage);
DataStorage *GetDataStorage();
// parameter setting
/// For any kind of normal types
template <typename T>
void SetParameter(const char *parameter, const T &value)
{
// MITK_INFO << "SetParameter(" << parameter << ") " << typeid(T).name() << std::endl;
// m_ParameterListMutex->Lock();
m_Parameters->SetProperty(parameter, GenericProperty<T>::New(value));
// m_ParameterListMutex->Unlock();
}
/// For any kind of smart pointers
template <typename T>
void SetPointerParameter(const char *parameter, const itk::SmartPointer<T> &value)
{
// MITK_INFO << this << "->SetParameter smartpointer(" << parameter << ") " << typeid(itk::SmartPointer<T>).name()
// << std::endl;
- m_ParameterListMutex->Lock();
+ m_ParameterListMutex.lock();
m_Parameters->SetProperty(parameter, SmartPointerProperty::New(value.GetPointer()));
- m_ParameterListMutex->Unlock();
+ m_ParameterListMutex.unlock();
}
// virtual void SetParameter( const char*, mitk::BaseProperty* ); // for "number of iterations", ...
// create some property observing to inform algorithm object about changes
// perhaps some TriggerParameter(string) macro that creates an observer for changes in a specific property like
// "2ndPoint" for LineAlgorithms
/// For any kind of BaseData, like Image, Surface, etc. Will be stored inside some SmartPointerProperty
void SetPointerParameter(const char *parameter, BaseData *value);
/// For any kind of ITK images (C pointers)
template <typename TPixel, unsigned int VImageDimension>
void SetItkImageAsMITKImagePointerParameter(const char *parameter, itk::Image<TPixel, VImageDimension> *itkImage)
{
// MITK_INFO << "SetParameter ITK image(" << parameter << ") " << typeid(itk::Image<TPixel,
// VImageDimension>).name() << std::endl;
// create an MITK image for that
mitk::Image::Pointer mitkImage = mitk::Image::New();
mitkImage = ImportItkImage(itkImage);
SetPointerParameter(parameter, mitkImage);
}
/// For any kind of ITK images (smartpointers)
template <typename TPixel, unsigned int VImageDimension>
void SetItkImageAsMITKImagePointerParameter(const char *parameter,
const itk::SmartPointer<itk::Image<TPixel, VImageDimension>> &itkImage)
{
// MITK_INFO << "SetParameter ITK image(" << parameter << ") " << typeid(itk::SmartPointer<itk::Image<TPixel,
// VImageDimension> >).name() << std::endl;
// create an MITK image for that
mitk::Image::Pointer mitkImage = mitk::Image::New();
mitkImage = ImportItkImage(itkImage);
SetPointerParameter(parameter, mitkImage);
}
// parameter getting
template <typename T>
void GetParameter(const char *parameter, T &value) const
{
// MITK_INFO << "GetParameter normal(" << parameter << ") " << typeid(T).name() << std::endl;
// m_ParameterListMutex->Lock();
BaseProperty *p = m_Parameters->GetProperty(parameter);
GenericProperty<T> *gp = dynamic_cast<GenericProperty<T> *>(p);
if (gp)
{
value = gp->GetValue();
// m_ParameterListMutex->Unlock();
return;
}
// m_ParameterListMutex->Unlock();
std::string error("There is no parameter \"");
error += parameter;
error += '"';
throw std::invalid_argument(error);
}
template <typename T>
void GetPointerParameter(const char *parameter, itk::SmartPointer<T> &value) const
{
// MITK_INFO << this << "->GetParameter smartpointer(" << parameter << ") " << typeid(itk::SmartPointer<T>).name()
// << std::endl;
// m_ParameterListMutex->Lock();
BaseProperty *p = m_Parameters->GetProperty(parameter);
if (p)
{
SmartPointerProperty *spp = dynamic_cast<SmartPointerProperty *>(p);
if (spp)
{
T *t = dynamic_cast<T *>(spp->GetSmartPointer().GetPointer());
value = t;
// m_ParameterListMutex->Unlock();
return;
}
}
// m_ParameterListMutex->Unlock();
std::string error("There is no parameter \"");
error += parameter;
error += '"';
throw std::invalid_argument(error);
}
// start/stop functions
virtual void Reset();
void StartAlgorithm(); // for those who want to trigger calculations on their own
// --> need for an OPTION: manual/automatic starting
void StartBlockingAlgorithm(); // for those who want to trigger calculations on their own
void StopAlgorithm();
void TriggerParameterModified(const itk::EventObject &);
void ThreadedUpdateSuccessful(const itk::EventObject &);
void ThreadedUpdateFailed(const itk::EventObject &);
protected:
NonBlockingAlgorithm(); // use smart pointers
~NonBlockingAlgorithm() override;
void DefineTriggerParameter(const char *);
void UnDefineTriggerParameter(const char *);
virtual void Initialize(const NonBlockingAlgorithm *other = nullptr);
virtual bool ReadyToRun();
virtual bool ThreadedUpdateFunction(); // will be called from a thread after calling StartAlgorithm
virtual void ThreadedUpdateSuccessful(); // will be called after the ThreadedUpdateFunction() returned
virtual void ThreadedUpdateFailed(); // will when ThreadedUpdateFunction() returns false
PropertyList::Pointer m_Parameters;
WeakPointer<DataStorage> m_DataStorage;
private:
- static ITK_THREAD_RETURN_TYPE StaticNonBlockingAlgorithmThread(void *param);
+ static itk::ITK_THREAD_RETURN_TYPE StaticNonBlockingAlgorithmThread(ThreadParameters *param);
typedef std::map<std::string, unsigned long> MapTypeStringUInt;
MapTypeStringUInt m_TriggerPropertyConnections;
- itk::FastMutexLock::Pointer m_ParameterListMutex;
+ std::mutex m_ParameterListMutex;
- int m_ThreadID;
int m_UpdateRequests;
ThreadParameters m_ThreadParameters;
- itk::MultiThreader::Pointer m_MultiThreader;
+ std::thread m_Thread;
bool m_KillRequest;
};
} // namespace
#include "mitkNonBlockingAlgorithmEvents.h"
#endif
diff --git a/Modules/AlgorithmsExt/src/mitkAnisotropicRegistrationCommon.cpp b/Modules/AlgorithmsExt/src/mitkAnisotropicRegistrationCommon.cpp
index e904b06752..00a8976653 100644
--- a/Modules/AlgorithmsExt/src/mitkAnisotropicRegistrationCommon.cpp
+++ b/Modules/AlgorithmsExt/src/mitkAnisotropicRegistrationCommon.cpp
@@ -1,100 +1,100 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <mitkAnisotropicRegistrationCommon.h>
#include <mitkPointSet.h>
#include <vtkPoints.h>
mitk::AnisotropicRegistrationCommon::WeightMatrix mitk::AnisotropicRegistrationCommon::CalculateWeightMatrix(
const CovarianceMatrix &sigma_X, const CovarianceMatrix &sigma_Y)
{
WeightMatrix returnValue;
WeightMatrix sum = sigma_X + sigma_Y;
- vnl_svd<double> svd(sum.GetVnlMatrix());
+ vnl_svd<double> svd(sum.GetVnlMatrix().as_ref());
WeightMatrix diag;
diag.Fill(0.0);
diag[0][0] = 1.0 / sqrt(svd.W(0));
diag[1][1] = 1.0 / sqrt(svd.W(1));
diag[2][2] = 1.0 / sqrt(svd.W(2));
WeightMatrix V; // convert vnl matrix to itk matrix...
for (unsigned int i = 0; i < 3; ++i)
for (unsigned int j = 0; j < 3; ++j)
V[i][j] = svd.V()[i][j];
// add weighting matrix for point j1 (corresponding to identity transform)
returnValue = V * diag * V.GetTranspose();
return returnValue;
}
void mitk::AnisotropicRegistrationCommon::TransformPoints(vtkPoints *src,
vtkPoints *dst,
const Rotation &rotation,
const Translation &translation)
{
#pragma omp parallel for
for (int i = 0; i < src->GetNumberOfPoints(); ++i)
{
double p_in[3];
double p_out[3];
src->GetPoint(i, p_in);
for (unsigned int j = 0; j < 3; ++j)
{
p_out[j] = p_in[0] * rotation[j][0] + p_in[1] * rotation[j][1] + p_in[2] * rotation[j][2] + translation[j];
}
dst->SetPoint(i, p_out);
}
}
void mitk::AnisotropicRegistrationCommon::PropagateMatrices(const MatrixList &src,
MatrixList &dst,
const Rotation &rotation)
{
const vnl_matrix_fixed<double, 3, 3> rotationT = rotation.GetTranspose();
#pragma omp parallel for
for (int i = 0; i < static_cast<int>(src.size()); ++i)
{
dst[i] = rotation * src[i] * rotationT;
}
}
double mitk::AnisotropicRegistrationCommon::ComputeTargetRegistrationError(const mitk::PointSet *movingTargets,
const mitk::PointSet *fixedTargets,
const Rotation &rotation,
const Translation &translation)
{
double tre = 0.0;
for (int i = 0; i < movingTargets->GetSize(); ++i)
{
mitk::Point3D pm = movingTargets->GetPoint(i);
mitk::Point3D ps = fixedTargets->GetPoint(i);
// transform point
pm = rotation * pm + translation;
const double dist =
(ps[0] - pm[0]) * (ps[0] - pm[0]) + (ps[1] - pm[1]) * (ps[1] - pm[1]) + (ps[2] - pm[2]) * (ps[2] - pm[2]);
tre += dist;
}
tre /= movingTargets->GetSize();
return sqrt(tre);
}
diff --git a/Modules/AlgorithmsExt/src/mitkAutoCropImageFilter.cpp b/Modules/AlgorithmsExt/src/mitkAutoCropImageFilter.cpp
index d5b7b29842..f892632904 100644
--- a/Modules/AlgorithmsExt/src/mitkAutoCropImageFilter.cpp
+++ b/Modules/AlgorithmsExt/src/mitkAutoCropImageFilter.cpp
@@ -1,362 +1,362 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkAutoCropImageFilter.h"
#include "mitkGeometry3D.h"
#include "mitkImageAccessByItk.h"
#include "mitkImageCast.h"
#include "mitkImageReadAccessor.h"
#include "mitkPlaneGeometry.h"
#include "mitkStatusBar.h"
#include <itkImageRegionConstIterator.h>
#include <itkRegionOfInterestImageFilter.h>
#include <mitkProportionalTimeGeometry.h>
mitk::AutoCropImageFilter::AutoCropImageFilter()
: m_BackgroundValue(0), m_MarginFactor(1.0), m_TimeSelector(nullptr), m_OverrideCroppingRegion(false)
{
}
mitk::AutoCropImageFilter::~AutoCropImageFilter()
{
}
template <typename TPixel, unsigned int VImageDimension>
void mitk::AutoCropImageFilter::ITKCrop3DImage(itk::Image<TPixel, VImageDimension> *inputItkImage,
unsigned int timestep)
{
if (inputItkImage == nullptr)
{
mitk::StatusBar::GetInstance()->DisplayErrorText(
"An internal error occurred. Can't convert Image. Please report to bugs@mitk.org");
MITK_ERROR << "image is nullptr...returning" << std::endl;
return;
}
typedef itk::Image<TPixel, VImageDimension> InternalImageType;
typedef typename InternalImageType::Pointer InternalImagePointer;
typedef itk::RegionOfInterestImageFilter<InternalImageType, InternalImageType> ROIFilterType;
typedef typename itk::RegionOfInterestImageFilter<InternalImageType, InternalImageType>::Pointer ROIFilterPointer;
InternalImagePointer outputItk = InternalImageType::New();
ROIFilterPointer roiFilter = ROIFilterType::New();
roiFilter->SetInput(0, inputItkImage);
roiFilter->SetRegionOfInterest(this->GetCroppingRegion());
roiFilter->Update();
outputItk = roiFilter->GetOutput();
outputItk->DisconnectPipeline();
mitk::Image::Pointer newMitkImage = mitk::Image::New();
mitk::CastToMitkImage(outputItk, newMitkImage);
MITK_INFO << "Crop-Output dimension: " << (newMitkImage->GetDimension() == 3)
<< " Filter-Output dimension: " << this->GetOutput()->GetDimension() << " Timestep: " << timestep;
mitk::ImageReadAccessor newMitkImgAcc(newMitkImage);
this->GetOutput()->SetVolume(newMitkImgAcc.GetData(), timestep);
}
void mitk::AutoCropImageFilter::GenerateOutputInformation()
{
mitk::Image::Pointer input = const_cast<mitk::Image *>(this->GetInput());
mitk::Image::Pointer output = this->GetOutput();
if (input->GetDimension() <= 2)
{
MITK_ERROR << "Only 3D any 4D images are supported." << std::endl;
return;
}
ComputeNewImageBounds();
if ((output->IsInitialized()) && (output->GetPipelineMTime() <= m_TimeOfHeaderInitialization.GetMTime()))
return;
itkDebugMacro(<< "GenerateOutputInformation()");
// PART I: initialize input requested region. We do this already here (and not
// later when GenerateInputRequestedRegion() is called), because we
// also need the information to setup the output.
// pre-initialize input-requested-region to largest-possible-region
// and correct time-region; spatial part will be cropped by
// bounding-box of bounding-object below
m_InputRequestedRegion = input->GetLargestPossibleRegion();
// build region out of index and size calculated in ComputeNewImageBounds()
mitk::SlicedData::IndexType index;
index[0] = m_RegionIndex[0];
index[1] = m_RegionIndex[1];
index[2] = m_RegionIndex[2];
index[3] = m_InputRequestedRegion.GetIndex()[3];
index[4] = m_InputRequestedRegion.GetIndex()[4];
mitk::SlicedData::SizeType size;
size[0] = m_RegionSize[0];
size[1] = m_RegionSize[1];
size[2] = m_RegionSize[2];
size[3] = m_InputRequestedRegion.GetSize()[3];
size[4] = m_InputRequestedRegion.GetSize()[4];
mitk::SlicedData::RegionType cropRegion(index, size);
// crop input-requested-region with cropping region computed from the image data
if (m_InputRequestedRegion.Crop(cropRegion) == false)
{
// crop not possible => do nothing: set time size to 0.
size.Fill(0);
m_InputRequestedRegion.SetSize(size);
return;
}
// set input-requested-region, because we access it later in
// GenerateInputRequestedRegion (there we just set the time)
input->SetRequestedRegion(&m_InputRequestedRegion);
// PART II: initialize output image
unsigned int dimension = input->GetDimension();
auto dimensions = new unsigned int[dimension];
itk2vtk(m_InputRequestedRegion.GetSize(), dimensions);
if (dimension > 3)
memcpy(dimensions + 3, input->GetDimensions() + 3, (dimension - 3) * sizeof(unsigned int));
// create basic slicedGeometry that will be initialized below
output->Initialize(mitk::PixelType(GetOutputPixelType()), dimension, dimensions);
delete[] dimensions;
// clone the IndexToWorldTransform from the input, otherwise we will overwrite it, when adjusting the origin of the
// output image!!
itk::ScalableAffineTransform<mitk::ScalarType, 3>::Pointer cloneTransform =
itk::ScalableAffineTransform<mitk::ScalarType, 3>::New();
cloneTransform->Compose(input->GetGeometry()->GetIndexToWorldTransform());
output->GetGeometry()->SetIndexToWorldTransform(cloneTransform.GetPointer());
// Position the output Image to match the corresponding region of the input image
mitk::SlicedGeometry3D *slicedGeometry = output->GetSlicedGeometry();
mitk::SlicedGeometry3D::Pointer inputGeometry = input->GetSlicedGeometry();
const mitk::SlicedData::IndexType &start = m_InputRequestedRegion.GetIndex();
mitk::Point3D origin;
vtk2itk(start, origin);
input->GetSlicedGeometry()->IndexToWorld(origin, origin);
slicedGeometry->SetOrigin(origin);
// get the PlaneGeometry for the first slice of the original image
mitk::PlaneGeometry::Pointer plane =
dynamic_cast<mitk::PlaneGeometry *>(inputGeometry->GetPlaneGeometry(0)->Clone().GetPointer());
assert(plane);
// re-initialize the plane according to the new requirements:
// dimensions of the cropped image
// right- and down-vector as well as spacing do not change, so use the ones from
// input image
ScalarType dimX = output->GetDimensions()[0];
ScalarType dimY = output->GetDimensions()[1];
mitk::Vector3D right = plane->GetAxisVector(0);
mitk::Vector3D down = plane->GetAxisVector(1);
mitk::Vector3D spacing = plane->GetSpacing();
plane->InitializeStandardPlane(dimX, dimY, right, down, &spacing);
// set the new origin on the PlaneGeometry as well
plane->SetOrigin(origin);
// re-initialize the slicedGeometry with the correct planeGeometry
// in order to get a fully initialized SlicedGeometry3D
slicedGeometry->InitializeEvenlySpaced(
plane, inputGeometry->GetSpacing()[2], output->GetSlicedGeometry()->GetSlices());
mitk::TimeGeometry *timeSlicedGeometry = output->GetTimeGeometry();
auto *propTimeGeometry = dynamic_cast<ProportionalTimeGeometry *>(timeSlicedGeometry);
propTimeGeometry->Initialize(slicedGeometry, output->GetDimension(3));
m_TimeOfHeaderInitialization.Modified();
output->SetPropertyList(input->GetPropertyList()->Clone());
}
void mitk::AutoCropImageFilter::GenerateData()
{
mitk::Image::ConstPointer input = this->GetInput();
mitk::Image::Pointer output = this->GetOutput();
if (input.IsNull())
return;
if (input->GetDimension() <= 2)
{
MITK_ERROR << "Only 3D and 4D images supported";
return;
}
if ((output->IsInitialized() == false))
return;
if (m_TimeSelector.IsNull())
m_TimeSelector = mitk::ImageTimeSelector::New();
m_TimeSelector->SetInput(input);
mitk::SlicedData::RegionType outputRegion = input->GetRequestedRegion();
int tstart = outputRegion.GetIndex(3);
int tmax = tstart + outputRegion.GetSize(3);
for (int timestep = tstart; timestep < tmax; ++timestep)
{
m_TimeSelector->SetTimeNr(timestep);
m_TimeSelector->UpdateLargestPossibleRegion();
AccessFixedDimensionByItk_1(m_TimeSelector->GetOutput(), ITKCrop3DImage, 3, timestep);
}
// this->GetOutput()->Update(); // Not sure if this is necessary...
m_TimeOfHeaderInitialization.Modified();
}
void mitk::AutoCropImageFilter::ComputeNewImageBounds()
{
mitk::Image::ConstPointer inputMitk = this->GetInput();
if (m_OverrideCroppingRegion)
{
for (unsigned int i = 0; i < 3; ++i)
{
m_RegionIndex[i] = m_CroppingRegion.GetIndex()[i];
m_RegionSize[i] = m_CroppingRegion.GetSize()[i];
if (m_RegionIndex[i] >= static_cast<RegionType::IndexValueType>(inputMitk->GetDimension(i)))
{
itkExceptionMacro("Cropping index is not inside the image. " << std::endl
<< "Index:"
<< std::endl
<< m_CroppingRegion.GetIndex()
<< std::endl
<< "Size:"
<< std::endl
<< m_CroppingRegion.GetSize());
}
if (m_RegionIndex[i] + m_RegionSize[i] >= inputMitk->GetDimension(i))
{
m_RegionSize[i] = inputMitk->GetDimension(i) - m_RegionIndex[i];
}
}
for (unsigned int i = 0; i < 3; ++i)
{
m_RegionIndex[i] = m_CroppingRegion.GetIndex()[i];
m_RegionSize[i] = m_CroppingRegion.GetSize()[i];
}
}
else
{
// Check if a 3D or 4D image is present
unsigned int timeSteps = 1;
if (inputMitk->GetDimension() == 4)
timeSteps = inputMitk->GetDimension(3);
ImageType::IndexType minima, maxima;
if (inputMitk->GetDimension() == 4)
{
// initialize with time step 0
m_TimeSelector = mitk::ImageTimeSelector::New();
m_TimeSelector->SetInput(inputMitk);
m_TimeSelector->SetTimeNr(0);
m_TimeSelector->UpdateLargestPossibleRegion();
inputMitk = m_TimeSelector->GetOutput();
}
ImagePointer inputItk = ImageType::New();
mitk::CastToItkImage(inputMitk, inputItk);
// it is assumed that all volumes in a time series have the same 3D dimensions
ImageType::RegionType origRegion = inputItk->GetLargestPossibleRegion();
// Initialize min and max on the first (or only) time step
maxima = inputItk->GetLargestPossibleRegion().GetIndex();
minima[0] = inputItk->GetLargestPossibleRegion().GetSize()[0];
minima[1] = inputItk->GetLargestPossibleRegion().GetSize()[1];
minima[2] = inputItk->GetLargestPossibleRegion().GetSize()[2];
typedef itk::ImageRegionConstIterator<ImageType> ConstIteratorType;
for (unsigned int idx = 0; idx < timeSteps; ++idx)
{
// if 4D image, update time step and itk image
if (idx > 0)
{
m_TimeSelector->SetTimeNr(idx);
m_TimeSelector->UpdateLargestPossibleRegion();
inputMitk = m_TimeSelector->GetOutput();
mitk::CastToItkImage(inputMitk, inputItk);
}
ConstIteratorType inIt(inputItk, origRegion);
for (inIt.GoToBegin(); !inIt.IsAtEnd(); ++inIt)
{
float pix_val = inIt.Get();
if (fabs(pix_val - m_BackgroundValue) > mitk::eps)
{
for (int i = 0; i < 3; i++)
{
- minima[i] = vnl_math_min((int)minima[i], (int)(inIt.GetIndex()[i]));
- maxima[i] = vnl_math_max((int)maxima[i], (int)(inIt.GetIndex()[i]));
+ minima[i] = std::min((int)minima[i], (int)(inIt.GetIndex()[i]));
+ maxima[i] = std::max((int)maxima[i], (int)(inIt.GetIndex()[i]));
}
}
}
}
typedef ImageType::RegionType::SizeType::SizeValueType SizeValueType;
m_RegionSize[0] = (SizeValueType)(m_MarginFactor * (maxima[0] - minima[0] + 1));
m_RegionSize[1] = (SizeValueType)(m_MarginFactor * (maxima[1] - minima[1] + 1));
m_RegionSize[2] = (SizeValueType)(m_MarginFactor * (maxima[2] - minima[2] + 1));
m_RegionIndex = minima;
m_RegionIndex[0] -= (m_RegionSize[0] - maxima[0] + minima[0] - 1) / 2;
m_RegionIndex[1] -= (m_RegionSize[1] - maxima[1] + minima[1] - 1) / 2;
m_RegionIndex[2] -= (m_RegionSize[2] - maxima[2] + minima[2] - 1) / 2;
ImageType::RegionType cropRegion(m_RegionIndex, m_RegionSize);
origRegion.Crop(cropRegion);
m_RegionSize[0] = origRegion.GetSize()[0];
m_RegionSize[1] = origRegion.GetSize()[1];
m_RegionSize[2] = origRegion.GetSize()[2];
m_RegionIndex[0] = origRegion.GetIndex()[0];
m_RegionIndex[1] = origRegion.GetIndex()[1];
m_RegionIndex[2] = origRegion.GetIndex()[2];
m_CroppingRegion = origRegion;
}
}
void mitk::AutoCropImageFilter::GenerateInputRequestedRegion()
{
}
const mitk::PixelType mitk::AutoCropImageFilter::GetOutputPixelType()
{
return this->GetInput()->GetPixelType();
}
void mitk::AutoCropImageFilter::SetCroppingRegion(RegionType overrideRegion)
{
m_CroppingRegion = overrideRegion;
m_OverrideCroppingRegion = true;
}
diff --git a/Modules/AlgorithmsExt/src/mitkCovarianceMatrixCalculator.cpp b/Modules/AlgorithmsExt/src/mitkCovarianceMatrixCalculator.cpp
index 3a59126040..e34ff3e2c3 100644
--- a/Modules/AlgorithmsExt/src/mitkCovarianceMatrixCalculator.cpp
+++ b/Modules/AlgorithmsExt/src/mitkCovarianceMatrixCalculator.cpp
@@ -1,412 +1,412 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkCovarianceMatrixCalculator.h"
#include <mitkExceptionMacro.h>
#include <mitkSurface.h>
#include <vtkCell.h>
#include <vtkCellLinks.h>
#include <vtkPlane.h>
#include <vtkPointData.h>
#include <vtkPolyDataNormals.h>
#include <vtkSmartPointer.h>
// forward declarations of private functions
static vtkIdList *GetNeighboursOfPoint(unsigned int index, vtkPolyData *polydata);
static vtkIdList *CalculatePCAonPointNeighboursForNormalVector(int index,
double normal[3],
itk::Matrix<double, 3, 3> &mat,
double curVertex[3],
std::vector<mitk::Point3D> &pointList,
vtkPolyData *polyData);
static itk::Matrix<double, 3, 3> ComputeCovarianceMatrix(itk::Matrix<double, 3, 3> &axes,
double sigma[3],
double normalizationValue);
namespace mitk
{
/** \brief Pimpl to hold the private data in the CovarianceMatrixCalculator.*/
struct CovarianceMatrixCalculatorData
{
vtkPolyDataNormals *m_PolyDataNormals;
vtkPolyData *m_PolyData;
Surface *m_Input;
double m_VoronoiScalingFactor;
bool m_EnableNormalization;
double m_MeanVariance;
CovarianceMatrixCalculatorData()
: m_PolyDataNormals(vtkPolyDataNormals::New()),
m_PolyData(nullptr),
m_Input(nullptr),
m_VoronoiScalingFactor(1.0),
m_EnableNormalization(false),
m_MeanVariance(0.0)
{
m_PolyDataNormals->SplittingOff();
}
~CovarianceMatrixCalculatorData()
{
if (m_PolyDataNormals)
m_PolyDataNormals->Delete();
}
};
}
mitk::CovarianceMatrixCalculator::CovarianceMatrixCalculator() : d(new CovarianceMatrixCalculatorData())
{
}
mitk::CovarianceMatrixCalculator::~CovarianceMatrixCalculator()
{
delete d;
}
void mitk::CovarianceMatrixCalculator::SetVoronoiScalingFator(const double factor)
{
d->m_VoronoiScalingFactor = factor;
}
void mitk::CovarianceMatrixCalculator::EnableNormalization(bool state)
{
d->m_EnableNormalization = state;
}
double mitk::CovarianceMatrixCalculator::GetMeanVariance() const
{
return d->m_MeanVariance;
}
const mitk::CovarianceMatrixCalculator::CovarianceMatrixList &mitk::CovarianceMatrixCalculator::GetCovarianceMatrices()
const
{
return m_CovarianceMatrixList;
}
void mitk::CovarianceMatrixCalculator::SetInputSurface(Surface *input)
{
d->m_Input = input;
}
void mitk::CovarianceMatrixCalculator::ComputeCovarianceMatrices()
{
double normalizationValue = -1.0;
vtkDataArray *normals = nullptr;
d->m_MeanVariance = 0.0;
if (!d->m_Input)
mitkThrow() << "No input surface was set in mitk::CovarianceMatrixCalculator";
d->m_PolyData = d->m_Input->GetVtkPolyData();
// Optional normal calculation can be disabled to use the normals
// of the surface:
// normals = d->m_PolyData->GetPointData()->GetNormals();
//// compute surface normals if the surface has no normals
// if ( normals == nullptr )
//{
d->m_PolyDataNormals->SetInputData(d->m_PolyData);
d->m_PolyDataNormals->Update();
normals = d->m_PolyDataNormals->GetOutput()->GetPointData()->GetNormals();
//}
if (d->m_EnableNormalization)
normalizationValue = 1.5;
// clear the matrixlist
m_CovarianceMatrixList.clear();
// allocate memory if required
if (d->m_PolyData->GetNumberOfPoints() > (vtkIdType)m_CovarianceMatrixList.capacity())
m_CovarianceMatrixList.reserve(d->m_PolyData->GetNumberOfPoints());
for (vtkIdType i = 0; i < d->m_PolyData->GetNumberOfPoints(); ++i)
{
Vertex normal;
Vertex currentVertex;
Vertex variances = {0.0, 0.0, 0.0};
CovarianceMatrix mat;
mat.Fill(0.0);
normals->GetTuple(i, normal);
d->m_PolyData->GetPoint(i, currentVertex);
ComputeOrthonormalCoordinateSystem(i, normal, mat, variances, currentVertex);
// use prefactor for sigma along surface
variances[0] = (d->m_VoronoiScalingFactor * variances[0]);
variances[1] = (d->m_VoronoiScalingFactor * variances[1]);
variances[2] = (d->m_VoronoiScalingFactor * variances[2]);
d->m_MeanVariance += (variances[0] + variances[1] + variances[2]);
// compute the covariance matrix and save it
const CovarianceMatrix covarianceMatrix = ComputeCovarianceMatrix(mat, variances, normalizationValue);
m_CovarianceMatrixList.push_back(covarianceMatrix);
}
if (d->m_EnableNormalization)
d->m_MeanVariance = normalizationValue / 3.0;
else
d->m_MeanVariance /= (3.0 * (double)d->m_PolyData->GetNumberOfPoints());
// reset input
d->m_PolyData = nullptr;
d->m_Input = nullptr;
}
// Get a list with the id's of all surrounding conected vertices
// to the current vertex at the given index in the polydata
vtkIdList *GetNeighboursOfPoint(unsigned int index, vtkPolyData *polydata)
{
vtkIdList *cellIds = vtkIdList::New();
vtkIdList *result = vtkIdList::New();
polydata->GetPointCells(index, cellIds);
for (vtkIdType j = 0; j < cellIds->GetNumberOfIds(); j++)
{
vtkIdList *newPoints = polydata->GetCell(cellIds->GetId(j))->GetPointIds();
for (vtkIdType k = 0; k < newPoints->GetNumberOfIds(); k++)
{
// if point has not yet been inserted add id
if (result->IsId(newPoints->GetId(k)) == -1)
{
result->InsertNextId(newPoints->GetId(k));
}
}
}
cellIds->Delete();
return result;
}
// Computes a primary component analysis of the surounding vertices
// of the verex at the current index.
vtkIdList *CalculatePCAonPointNeighboursForNormalVector(int index,
double normal[3],
itk::Matrix<double, 3, 3> &mat,
double curVertex[3],
std::vector<mitk::Point3D> &pointList,
vtkPolyData *polyData)
{
typedef std::vector<mitk::Point3D> VectorType;
typedef VectorType::const_iterator ConstPointIterator;
typedef double Vertex[3];
Vertex mean = {0.0, 0.0, 0.0};
Vertex tmp = {0.0, 0.0, 0.0};
vtkIdList *neighbourPoints = GetNeighboursOfPoint(index, polyData);
const vtkIdType size = neighbourPoints->GetNumberOfIds();
// reserve memory for all neighbours
pointList.reserve(size);
// project neighbours on plane given by normal
// and compute mean
for (vtkIdType i = 0; i < size; ++i)
{
mitk::Point3D p;
Vertex resultPoint;
polyData->GetPoint((neighbourPoints->GetId(i)), tmp);
vtkPlane::GeneralizedProjectPoint(tmp, curVertex, normal, resultPoint);
p[0] = resultPoint[0];
p[1] = resultPoint[1];
p[2] = resultPoint[2];
mean[0] += p[0];
mean[1] += p[1];
mean[2] += p[2];
pointList.push_back(p);
}
mean[0] /= (double)size;
mean[1] /= (double)size;
mean[2] /= (double)size;
// compute the covariances with matrix multiplication
for (ConstPointIterator it = pointList.begin(); it != pointList.end(); ++it)
{
tmp[0] = ((*it)[0] - mean[0]);
tmp[1] = ((*it)[1] - mean[1]);
tmp[2] = ((*it)[2] - mean[2]);
// on diagonal elements
mat[0][0] += tmp[0] * tmp[0];
mat[1][1] += tmp[1] * tmp[1];
mat[2][2] += tmp[2] * tmp[2];
// of diagonal elements
mat[1][0] += tmp[0] * tmp[1];
mat[2][0] += tmp[0] * tmp[2];
mat[2][1] += tmp[1] * tmp[2];
}
// copy upper triangle to lower triangle,
// we got a symetric matrix
mat[0][1] = mat[1][0];
mat[0][2] = mat[2][0];
mat[1][2] = mat[2][1];
// variance
mat /= (size - 1);
- vnl_svd<double> svd(mat.GetVnlMatrix());
+ vnl_svd<double> svd(mat.GetVnlMatrix().as_ref());
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j)
mat[i][j] = svd.U()[j][i];
return neighbourPoints;
}
// Computes an orthonormal system for a vertex with it's surrounding neighbours.
void mitk::CovarianceMatrixCalculator::ComputeOrthonormalCoordinateSystem(
const int index, Vertex normal, CovarianceMatrix &axes, Vertex variances, Vertex curVertex)
{
typedef std::vector<mitk::Point3D> VectorType;
typedef VectorType::const_iterator ConstPointIterator;
VectorType projectedPoints;
Vertex meanValues = {0.0, 0.0, 0.0};
// project neighbours to new coordinate system and get principal axes
vtkIdList *neighbourPoints =
CalculatePCAonPointNeighboursForNormalVector(index, normal, axes, curVertex, projectedPoints, d->m_PolyData);
// Set the normal as the third principal axis
axes[2][0] = normal[0];
axes[2][1] = normal[1];
axes[2][2] = normal[2];
for (vtkIdType i = 0; i < neighbourPoints->GetNumberOfIds(); ++i)
{
mitk::Point3D projectedPoint;
Vertex curNeighbour;
d->m_PolyData->GetPoint(neighbourPoints->GetId(i), curNeighbour);
curNeighbour[0] = curNeighbour[0] - curVertex[0];
curNeighbour[1] = curNeighbour[1] - curVertex[1];
curNeighbour[2] = curNeighbour[2] - curVertex[2];
for (int k = 0; k < 3; ++k)
{
projectedPoint[k] = axes[k][0] * curNeighbour[0] + axes[k][1] * curNeighbour[1] + axes[k][2] * curNeighbour[2];
meanValues[k] += projectedPoint[k];
}
// reuse the allocated vector from the PCA on the point neighbours
projectedPoints[i] = projectedPoint;
}
meanValues[0] /= (double)projectedPoints.size();
meanValues[1] /= (double)projectedPoints.size();
meanValues[2] /= (double)projectedPoints.size();
// compute variances along new axes
for (ConstPointIterator it = projectedPoints.begin(); it != projectedPoints.end(); ++it)
{
const mitk::Point3D &p = *it;
variances[0] += (p[0] - meanValues[0]) * (p[0] - meanValues[0]);
variances[1] += (p[1] - meanValues[1]) * (p[1] - meanValues[1]);
variances[2] += (p[2] - meanValues[2]) * (p[2] - meanValues[2]);
}
variances[0] /= (double)(projectedPoints.size() - 1);
variances[1] /= (double)(projectedPoints.size() - 1);
variances[2] /= (double)(projectedPoints.size() - 1);
// clean up
neighbourPoints->Delete();
}
// Sorts the axes of the computed orthonormal system based on
// the eigenvalues in a descending order
itk::Matrix<double, 3, 3> ComputeCovarianceMatrix(itk::Matrix<double, 3, 3> &axes,
double sigma[3],
double normalizationValue)
{
unsigned int idxMax, idxMin, idxBetween;
itk::Matrix<double, 3, 3> returnValue;
itk::Matrix<double, 3, 3> V;
itk::Matrix<double, 3, 3> diagMatrix;
diagMatrix.Fill(0.0);
if (sigma[0] >= sigma[1] && sigma[0] >= sigma[2])
{
idxMax = 0;
if (sigma[1] >= sigma[2])
{
idxBetween = 1;
idxMin = 2;
}
else
{
idxBetween = 2;
idxMin = 1;
}
}
else if (sigma[1] >= sigma[0] && sigma[1] >= sigma[2])
{
idxMax = 1;
if (sigma[0] >= sigma[2])
{
idxBetween = 0;
idxMin = 2;
}
else
{
idxBetween = 2;
idxMin = 0;
}
}
else // index 2 corresponds to largest sigma
{
idxMax = 2;
if (sigma[0] >= sigma[1])
{
idxBetween = 0;
idxMin = 1;
}
else
{
idxBetween = 1;
idxMin = 0;
}
}
V[0][0] = axes[idxMax][0];
V[1][0] = axes[idxMax][1];
V[2][0] = axes[idxMax][2];
V[0][1] = axes[idxBetween][0];
V[1][1] = axes[idxBetween][1];
V[2][1] = axes[idxBetween][2];
V[0][2] = axes[idxMin][0];
V[1][2] = axes[idxMin][1];
V[2][2] = axes[idxMin][2];
diagMatrix[0][0] = sigma[idxMax];
diagMatrix[1][1] = sigma[idxBetween];
diagMatrix[2][2] = sigma[idxMin];
returnValue = V * diagMatrix * V.GetTranspose();
if (normalizationValue > 0.0)
{
double trace = returnValue[0][0] + returnValue[1][1] + returnValue[2][2];
returnValue *= (normalizationValue / trace);
}
return returnValue;
}
diff --git a/Modules/AlgorithmsExt/src/mitkCropTimestepsImageFilter.cpp b/Modules/AlgorithmsExt/src/mitkCropTimestepsImageFilter.cpp
index d1c2edf31d..a19cad722b 100644
--- a/Modules/AlgorithmsExt/src/mitkCropTimestepsImageFilter.cpp
+++ b/Modules/AlgorithmsExt/src/mitkCropTimestepsImageFilter.cpp
@@ -1,158 +1,158 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkCropTimestepsImageFilter.h"
#include <mitkImage.h>
#include <mitkArbitraryTimeGeometry.h>
#include <mitkImageTimeSelector.h>
#include <mitkImageReadAccessor.h>
void mitk::CropTimestepsImageFilter::VerifyInputImage(const mitk::Image* inputImage) const
{
if (!inputImage->IsInitialized())
mitkThrow() << "Input image is not initialized.";
if (!inputImage->IsVolumeSet())
mitkThrow() << "Input image volume is not set.";
auto geometry = inputImage->GetGeometry();
if (nullptr == geometry || !geometry->IsValid())
mitkThrow() << "Input image has invalid geometry.";
if (inputImage->GetDimension() != 4) {
mitkThrow() << "CropTimestepsImageFilter only works with 2D+t and 3D+t images.";
}
if (inputImage->GetTimeSteps() ==1) {
mitkThrow() << "Input image has only one timestep.";
}
if (!geometry->GetImageGeometry())
mitkThrow() << "Geometry of input image is not an image geometry.";
}
void mitk::CropTimestepsImageFilter::GenerateOutputInformation()
{
Image::ConstPointer input = this->GetInput();
Image::Pointer output = this->GetOutput();
if (m_LowerBoundaryTimestep > m_UpperBoundaryTimestep) {
mitkThrow() << "lower timestep is larger than upper timestep.";
}
if (m_UpperBoundaryTimestep == std::numeric_limits<unsigned int>::max()) {
m_UpperBoundaryTimestep = input->GetTimeSteps();
}
else if (m_UpperBoundaryTimestep > input->GetTimeSteps()) {
m_UpperBoundaryTimestep = input->GetTimeSteps();
MITK_WARN << "upper boundary timestep set to " << m_UpperBoundaryTimestep;
}
m_DesiredRegion = ComputeDesiredRegion();
unsigned int dimension = input->GetDimension();
auto dimensions = new unsigned int[dimension];
itk2vtk(m_DesiredRegion.GetSize(), dimensions);
if (dimension > 3)
memcpy(dimensions + 3, input->GetDimensions() + 3, (dimension - 3) * sizeof(unsigned int));
dimensions[3] = m_UpperBoundaryTimestep - m_LowerBoundaryTimestep;
// create basic slicedGeometry that will be initialized below
output->Initialize(mitk::PixelType(input->GetPixelType()), dimension, dimensions);
delete[] dimensions;
auto newTimeGeometry = AdaptTimeGeometry(input->GetTimeGeometry(), m_LowerBoundaryTimestep, m_UpperBoundaryTimestep);
output->SetTimeGeometry(newTimeGeometry);
output->SetPropertyList(input->GetPropertyList());
}
mitk::SlicedData::RegionType mitk::CropTimestepsImageFilter::ComputeDesiredRegion() const
{
auto desiredRegion = this->GetInput()->GetLargestPossibleRegion();
auto index = desiredRegion.GetIndex();
auto size = desiredRegion.GetSize();
unsigned int timeDimension = 3;
index[timeDimension] = m_LowerBoundaryTimestep;
size[timeDimension] = m_UpperBoundaryTimestep - m_LowerBoundaryTimestep;
desiredRegion.SetIndex(index);
desiredRegion.SetSize(size);
return desiredRegion;
}
mitk::TimeGeometry::Pointer mitk::CropTimestepsImageFilter::AdaptTimeGeometry(mitk::TimeGeometry::ConstPointer sourceGeometry, unsigned int startTimestep, unsigned int endTimestep) const
{
auto newTimeGeometry = mitk::ArbitraryTimeGeometry::New();
newTimeGeometry->ClearAllGeometries();
for (unsigned int timestep = startTimestep; timestep < endTimestep; timestep++)
{
auto geometryForTimePoint = sourceGeometry->GetGeometryForTimeStep(timestep);
auto minTP = sourceGeometry->GetMinimumTimePoint(timestep);
auto maxTP = sourceGeometry->GetMaximumTimePoint(timestep);
///////////////////////////////////////
// Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details.
// This workarround should be removed as soon as T28262 is solved!
if (timestep + 1 == sourceGeometry->CountTimeSteps() && minTP == maxTP)
{
maxTP = minTP + 1.;
}
// End of workarround for T27883
//////////////////////////////////////
newTimeGeometry->AppendNewTimeStepClone(geometryForTimePoint, minTP, maxTP);
}
return newTimeGeometry.GetPointer();
}
void mitk::CropTimestepsImageFilter::GenerateData()
{
const auto* inputImage = this->GetInput();
mitk::Image::Pointer output = this->GetOutput();
if ((output->IsInitialized() == false))
return;
auto timeSelector = mitk::ImageTimeSelector::New();
timeSelector->SetInput(inputImage);
unsigned int timeStart = m_DesiredRegion.GetIndex(3);
unsigned int timeEnd = timeStart + m_DesiredRegion.GetSize(3);
for (unsigned int timestep = timeStart; timestep < timeEnd; ++timestep)
{
timeSelector->SetTimeNr(timestep);
timeSelector->UpdateLargestPossibleRegion();
mitk::ImageReadAccessor imageAccessorWithOneTimestep(timeSelector->GetOutput());
output->SetVolume(imageAccessorWithOneTimestep.GetData(), timestep-timeStart);
}
}
void mitk::CropTimestepsImageFilter::SetInput(const InputImageType* image)
{
if (this->GetInput() == image)
return;
Superclass::SetInput(image);
}
void mitk::CropTimestepsImageFilter::SetInput(unsigned int index, const InputImageType* image)
{
if (0 != index)
mitkThrow() << "Input index " << index << " is invalid.";
this->SetInput(image);
}
-void mitk::CropTimestepsImageFilter::VerifyInputInformation()
+void mitk::CropTimestepsImageFilter::VerifyInputInformation() const
{
Superclass::VerifyInputInformation();
VerifyInputImage(this->GetInput());
}
diff --git a/Modules/AlgorithmsExt/src/mitkMaskImageFilter.cpp b/Modules/AlgorithmsExt/src/mitkMaskImageFilter.cpp
index d08b3c1ddc..c4d13bff6c 100644
--- a/Modules/AlgorithmsExt/src/mitkMaskImageFilter.cpp
+++ b/Modules/AlgorithmsExt/src/mitkMaskImageFilter.cpp
@@ -1,241 +1,241 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkMaskImageFilter.h"
#include "mitkImageTimeSelector.h"
#include "mitkProperties.h"
#include "mitkTimeHelper.h"
#include "mitkImageAccessByItk.h"
#include "mitkImageToItk.h"
#include "itkImageRegionConstIterator.h"
#include "itkImageRegionIteratorWithIndex.h"
#include <limits>
mitk::MaskImageFilter::MaskImageFilter() : m_Mask(nullptr)
{
this->SetNumberOfIndexedInputs(2);
this->SetNumberOfRequiredInputs(2);
m_InputTimeSelector = mitk::ImageTimeSelector::New();
m_MaskTimeSelector = mitk::ImageTimeSelector::New();
m_OutputTimeSelector = mitk::ImageTimeSelector::New();
m_OverrideOutsideValue = false;
m_OutsideValue = 0;
}
mitk::MaskImageFilter::~MaskImageFilter()
{
}
void mitk::MaskImageFilter::SetMask(const mitk::Image *mask)
{
// Process object is not const-correct so the const_cast is required here
m_Mask = const_cast<mitk::Image *>(mask);
this->ProcessObject::SetNthInput(1, m_Mask);
}
const mitk::Image *mitk::MaskImageFilter::GetMask() const
{
return m_Mask;
}
void mitk::MaskImageFilter::GenerateInputRequestedRegion()
{
Superclass::GenerateInputRequestedRegion();
mitk::Image *output = this->GetOutput();
mitk::Image *input = this->GetInput();
mitk::Image *mask = m_Mask;
if ((output->IsInitialized() == false) || (mask == nullptr) || (mask->GetTimeGeometry()->CountTimeSteps() == 0))
return;
input->SetRequestedRegionToLargestPossibleRegion();
mask->SetRequestedRegionToLargestPossibleRegion();
GenerateTimeInInputRegion(output, input);
GenerateTimeInInputRegion(output, mask);
}
void mitk::MaskImageFilter::GenerateOutputInformation()
{
mitk::Image::ConstPointer input = this->GetInput();
mitk::Image::Pointer output = this->GetOutput();
if ((output->IsInitialized()) && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime()))
return;
itkDebugMacro(<< "GenerateOutputInformation()");
output->Initialize(input->GetPixelType(), *input->GetTimeGeometry());
output->SetPropertyList(input->GetPropertyList()->Clone());
m_TimeOfHeaderInitialization.Modified();
}
template <typename TPixel, unsigned int VImageDimension>
void mitk::MaskImageFilter::InternalComputeMask(itk::Image<TPixel, VImageDimension> *inputItkImage)
{
// dirty quick fix, duplicating code so both unsigned char and unsigned short are supported
// this should be changed once unsigned char segmentations can be converted to unsigned short
mitk::PixelType pixelType =
m_MaskTimeSelector->GetOutput()->GetImageDescriptor()->GetChannelDescriptor().GetPixelType();
- if (pixelType.GetComponentType() == itk::ImageIOBase::UCHAR)
+ if (pixelType.GetComponentType() == itk::IOComponentEnum::UCHAR)
{
typedef itk::Image<TPixel, VImageDimension> ItkInputImageType;
typedef itk::Image<unsigned char, VImageDimension> ItkMaskImageType;
typedef itk::Image<TPixel, VImageDimension> ItkOutputImageType;
typedef itk::ImageRegionConstIterator<ItkInputImageType> ItkInputImageIteratorType;
typedef itk::ImageRegionConstIterator<ItkMaskImageType> ItkMaskImageIteratorType;
typedef itk::ImageRegionIteratorWithIndex<ItkOutputImageType> ItkOutputImageIteratorType;
typename mitk::ImageToItk<ItkMaskImageType>::Pointer maskimagetoitk = mitk::ImageToItk<ItkMaskImageType>::New();
maskimagetoitk->SetInput(m_MaskTimeSelector->GetOutput());
maskimagetoitk->Update();
typename ItkMaskImageType::Pointer maskItkImage = maskimagetoitk->GetOutput();
typename mitk::ImageToItk<ItkOutputImageType>::Pointer outputimagetoitk =
mitk::ImageToItk<ItkOutputImageType>::New();
outputimagetoitk->SetInput(m_OutputTimeSelector->GetOutput());
outputimagetoitk->Update();
typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput();
// create the iterators
typename ItkInputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion();
ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest);
ItkMaskImageIteratorType maskIt(maskItkImage, inputRegionOfInterest);
ItkOutputImageIteratorType outputIt(outputItkImage, inputRegionOfInterest);
// typename ItkOutputImageType::PixelType outsideValue = itk::NumericTraits<typename
// ItkOutputImageType::PixelType>::min();
if (!m_OverrideOutsideValue)
m_OutsideValue = itk::NumericTraits<typename ItkOutputImageType::PixelType>::min();
m_MinValue = std::numeric_limits<mitk::ScalarType>::max();
m_MaxValue = std::numeric_limits<mitk::ScalarType>::min();
for (inputIt.GoToBegin(), maskIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd() && !maskIt.IsAtEnd();
++inputIt, ++maskIt, ++outputIt)
{
if (maskIt.Get() > itk::NumericTraits<typename ItkMaskImageType::PixelType>::Zero)
{
outputIt.Set(inputIt.Get());
- m_MinValue = vnl_math_min((float)inputIt.Get(), (float)m_MinValue);
- m_MaxValue = vnl_math_max((float)inputIt.Get(), (float)m_MaxValue);
+ m_MinValue = std::min((float)inputIt.Get(), (float)m_MinValue);
+ m_MaxValue = std::max((float)inputIt.Get(), (float)m_MaxValue);
}
else
{
outputIt.Set(m_OutsideValue);
}
}
}
else
{
{
typedef itk::Image<TPixel, VImageDimension> ItkInputImageType;
typedef itk::Image<unsigned short, VImageDimension> ItkMaskImageType;
typedef itk::Image<TPixel, VImageDimension> ItkOutputImageType;
typedef itk::ImageRegionConstIterator<ItkInputImageType> ItkInputImageIteratorType;
typedef itk::ImageRegionConstIterator<ItkMaskImageType> ItkMaskImageIteratorType;
typedef itk::ImageRegionIteratorWithIndex<ItkOutputImageType> ItkOutputImageIteratorType;
typename mitk::ImageToItk<ItkMaskImageType>::Pointer maskimagetoitk = mitk::ImageToItk<ItkMaskImageType>::New();
maskimagetoitk->SetInput(m_MaskTimeSelector->GetOutput());
maskimagetoitk->Update();
typename ItkMaskImageType::Pointer maskItkImage = maskimagetoitk->GetOutput();
typename mitk::ImageToItk<ItkOutputImageType>::Pointer outputimagetoitk =
mitk::ImageToItk<ItkOutputImageType>::New();
outputimagetoitk->SetInput(m_OutputTimeSelector->GetOutput());
outputimagetoitk->Update();
typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput();
// create the iterators
typename ItkInputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion();
ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest);
ItkMaskImageIteratorType maskIt(maskItkImage, inputRegionOfInterest);
ItkOutputImageIteratorType outputIt(outputItkImage, inputRegionOfInterest);
// typename ItkOutputImageType::PixelType outsideValue = itk::NumericTraits<typename
// ItkOutputImageType::PixelType>::min();
if (!m_OverrideOutsideValue)
m_OutsideValue = itk::NumericTraits<typename ItkOutputImageType::PixelType>::min();
m_MinValue = std::numeric_limits<mitk::ScalarType>::max();
m_MaxValue = std::numeric_limits<mitk::ScalarType>::min();
for (inputIt.GoToBegin(), maskIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd() && !maskIt.IsAtEnd();
++inputIt, ++maskIt, ++outputIt)
{
if (maskIt.Get() > itk::NumericTraits<typename ItkMaskImageType::PixelType>::Zero)
{
outputIt.Set(inputIt.Get());
- m_MinValue = vnl_math_min((float)inputIt.Get(), (float)m_MinValue);
- m_MaxValue = vnl_math_max((float)inputIt.Get(), (float)m_MaxValue);
+ m_MinValue = std::min((float)inputIt.Get(), (float)m_MinValue);
+ m_MaxValue = std::max((float)inputIt.Get(), (float)m_MaxValue);
}
else
{
outputIt.Set(m_OutsideValue);
}
}
}
}
}
void mitk::MaskImageFilter::GenerateData()
{
mitk::Image::ConstPointer input = this->GetInput();
mitk::Image::Pointer mask = m_Mask;
mitk::Image::Pointer output = this->GetOutput();
if ((output->IsInitialized() == false) || (mask.IsNull()) || (mask->GetTimeGeometry()->CountTimeSteps() == 0))
return;
m_InputTimeSelector->SetInput(input);
m_MaskTimeSelector->SetInput(mask);
m_OutputTimeSelector->SetInput(this->GetOutput());
mitk::Image::RegionType outputRegion = output->GetRequestedRegion();
const mitk::TimeGeometry *outputTimeGeometry = output->GetTimeGeometry();
const mitk::TimeGeometry *inputTimeGeometry = input->GetTimeGeometry();
const mitk::TimeGeometry *maskTimeGeometry = mask->GetTimeGeometry();
ScalarType timeInMS;
int timestep = 0;
int tstart = outputRegion.GetIndex(3);
int tmax = tstart + outputRegion.GetSize(3);
int t;
for (t = tstart; t < tmax; ++t)
{
timeInMS = outputTimeGeometry->TimeStepToTimePoint(t);
timestep = inputTimeGeometry->TimePointToTimeStep(timeInMS);
m_InputTimeSelector->SetTimeNr(timestep);
m_InputTimeSelector->UpdateLargestPossibleRegion();
m_OutputTimeSelector->SetTimeNr(t);
m_OutputTimeSelector->UpdateLargestPossibleRegion();
timestep = maskTimeGeometry->TimePointToTimeStep(timeInMS);
m_MaskTimeSelector->SetTimeNr(timestep);
m_MaskTimeSelector->UpdateLargestPossibleRegion();
AccessByItk(m_InputTimeSelector->GetOutput(), InternalComputeMask);
}
m_TimeOfHeaderInitialization.Modified();
}
diff --git a/Modules/AlgorithmsExt/src/mitkNonBlockingAlgorithm.cpp b/Modules/AlgorithmsExt/src/mitkNonBlockingAlgorithm.cpp
index ea6eb1135d..7459b1e4ac 100644
--- a/Modules/AlgorithmsExt/src/mitkNonBlockingAlgorithm.cpp
+++ b/Modules/AlgorithmsExt/src/mitkNonBlockingAlgorithm.cpp
@@ -1,203 +1,189 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkNonBlockingAlgorithm.h"
#include "mitkCallbackFromGUIThread.h"
#include "mitkDataStorage.h"
#include <itkCommand.h>
namespace mitk
{
- NonBlockingAlgorithm::NonBlockingAlgorithm() : m_ThreadID(-1), m_UpdateRequests(0), m_KillRequest(false)
+ NonBlockingAlgorithm::NonBlockingAlgorithm() : m_UpdateRequests(0), m_KillRequest(false)
{
- m_ParameterListMutex = itk::FastMutexLock::New();
m_Parameters = PropertyList::New();
- m_MultiThreader = itk::MultiThreader::New();
}
- NonBlockingAlgorithm::~NonBlockingAlgorithm() {}
+ NonBlockingAlgorithm::~NonBlockingAlgorithm()
+ {
+ if (m_Thread.joinable())
+ m_Thread.join();
+ }
+
void mitk::NonBlockingAlgorithm::SetDataStorage(DataStorage &storage) { m_DataStorage = &storage; }
DataStorage *mitk::NonBlockingAlgorithm::GetDataStorage() { return m_DataStorage.Lock(); }
void NonBlockingAlgorithm::Initialize(const NonBlockingAlgorithm *itkNotUsed(other))
{
// define one input, one output basedata object
// some basedata input - image, surface, whatever
BaseData::Pointer input;
SetPointerParameter("Input", input);
// some basedata output
BaseData::Pointer output;
SetPointerParameter("Output", output);
}
void NonBlockingAlgorithm::SetPointerParameter(const char *parameter, BaseData *value)
{
- m_ParameterListMutex->Lock();
+ m_ParameterListMutex.lock();
m_Parameters->SetProperty(parameter, SmartPointerProperty::New(value));
- m_ParameterListMutex->Unlock();
+ m_ParameterListMutex.unlock();
}
void NonBlockingAlgorithm::DefineTriggerParameter(const char *parameter)
{
BaseProperty *value = m_Parameters->GetProperty(parameter);
if (value && m_TriggerPropertyConnections.find(parameter) == m_TriggerPropertyConnections.end())
{
itk::ReceptorMemberCommand<NonBlockingAlgorithm>::Pointer command =
itk::ReceptorMemberCommand<NonBlockingAlgorithm>::New();
command->SetCallbackFunction(this, &NonBlockingAlgorithm::TriggerParameterModified);
m_TriggerPropertyConnections[parameter] = value->AddObserver(itk::ModifiedEvent(), command);
}
}
void NonBlockingAlgorithm::UnDefineTriggerParameter(const char *parameter)
{
auto iter = m_TriggerPropertyConnections.find(parameter);
if (iter != m_TriggerPropertyConnections.end())
{
BaseProperty *value = m_Parameters->GetProperty(parameter);
MITK_ERROR(!value) << "NonBlockingAlgorithm::UnDefineTriggerProperty() in bad state." << std::endl;
;
value->RemoveObserver(m_TriggerPropertyConnections[parameter]);
m_TriggerPropertyConnections.erase(iter);
}
}
void NonBlockingAlgorithm::Reset() { Initialize(); }
void NonBlockingAlgorithm::StartBlockingAlgorithm()
{
StartAlgorithm();
StopAlgorithm();
}
void NonBlockingAlgorithm::StartAlgorithm()
{
if (!ReadyToRun())
return; // let algorithm check if all input/parameters are ok
if (m_KillRequest)
return; // someone wants us to die
- m_ParameterListMutex->Lock();
+ m_ParameterListMutex.lock();
m_ThreadParameters.m_Algorithm = this;
++m_UpdateRequests;
- m_ParameterListMutex->Unlock();
- if (m_ThreadID != -1) // thread already running. But something obviously wants us to recalculate the output
+ m_ParameterListMutex.unlock();
+ if (m_Thread.joinable()) // thread already running. But something obviously wants us to recalculate the output
{
return; // thread already running
}
// spawn a thread that calls ThreadedUpdateFunction(), and ThreadedUpdateFinished() on us
- itk::ThreadFunctionType fpointer = &StaticNonBlockingAlgorithmThread;
- m_ThreadID = m_MultiThreader->SpawnThread(fpointer, &m_ThreadParameters);
+ m_Thread = std::thread(StaticNonBlockingAlgorithmThread, &m_ThreadParameters);
}
void NonBlockingAlgorithm::StopAlgorithm()
{
- if (m_ThreadID == -1)
- return; // thread not running
-
- m_MultiThreader->TerminateThread(m_ThreadID); // waits for the thread to terminate on its own
+ if (m_Thread.joinable())
+ m_Thread.join(); // waits for the thread to terminate on its own
}
// a static function to call a member of NonBlockingAlgorithm from inside an ITK thread
- ITK_THREAD_RETURN_TYPE NonBlockingAlgorithm::StaticNonBlockingAlgorithmThread(void *param)
+ itk::ITK_THREAD_RETURN_TYPE NonBlockingAlgorithm::StaticNonBlockingAlgorithmThread(ThreadParameters *param)
{
- // itk::MultiThreader provides an itk::MultiThreader::ThreadInfoStruct as parameter
- auto *itkmttis = static_cast<itk::MultiThreader::ThreadInfoStruct *>(param);
-
- // we need the UserData part of that structure
- auto *flsp = static_cast<ThreadParameters *>(itkmttis->UserData);
-
- NonBlockingAlgorithm::Pointer algorithm = flsp->m_Algorithm;
+ NonBlockingAlgorithm::Pointer algorithm = param->m_Algorithm;
// this UserData tells us, which BubbleTool's method to call
if (!algorithm)
{
- return ITK_THREAD_RETURN_VALUE;
+ return itk::ITK_THREAD_RETURN_DEFAULT_VALUE;
}
- algorithm->m_ParameterListMutex->Lock();
+ algorithm->m_ParameterListMutex.lock();
while (algorithm->m_UpdateRequests > 0)
{
algorithm->m_UpdateRequests = 0;
- algorithm->m_ParameterListMutex->Unlock();
+ algorithm->m_ParameterListMutex.unlock();
// actually call the methods that do the work
if (algorithm->ThreadedUpdateFunction()) // returns a bool for success/failure
{
itk::ReceptorMemberCommand<NonBlockingAlgorithm>::Pointer command =
itk::ReceptorMemberCommand<NonBlockingAlgorithm>::New();
command->SetCallbackFunction(algorithm, &NonBlockingAlgorithm::ThreadedUpdateSuccessful);
CallbackFromGUIThread::GetInstance()->CallThisFromGUIThread(command);
// algorithm->ThreadedUpdateSuccessful();
}
else
{
itk::ReceptorMemberCommand<NonBlockingAlgorithm>::Pointer command =
itk::ReceptorMemberCommand<NonBlockingAlgorithm>::New();
command->SetCallbackFunction(algorithm, &NonBlockingAlgorithm::ThreadedUpdateFailed);
CallbackFromGUIThread::GetInstance()->CallThisFromGUIThread(command);
// algorithm->ThreadedUpdateFailed();
}
- algorithm->m_ParameterListMutex->Lock();
+ algorithm->m_ParameterListMutex.lock();
}
- algorithm->m_ParameterListMutex->Unlock();
+ algorithm->m_ParameterListMutex.unlock();
- return ITK_THREAD_RETURN_VALUE;
+ return ITK_THREAD_RETURN_DEFAULT_VALUE;
}
void NonBlockingAlgorithm::TriggerParameterModified(const itk::EventObject &) { StartAlgorithm(); }
bool NonBlockingAlgorithm::ReadyToRun()
{
return true; // default is always ready
}
bool NonBlockingAlgorithm::ThreadedUpdateFunction() { return true; }
// called from gui thread
void NonBlockingAlgorithm::ThreadedUpdateSuccessful(const itk::EventObject &)
{
ThreadedUpdateSuccessful();
-
- m_ParameterListMutex->Lock();
- m_ThreadID = -1; // tested before starting
- m_ParameterListMutex->Unlock();
m_ThreadParameters.m_Algorithm = nullptr;
}
void NonBlockingAlgorithm::ThreadedUpdateSuccessful()
{
// notify observers that a result is ready
InvokeEvent(ResultAvailable(this));
}
// called from gui thread
void NonBlockingAlgorithm::ThreadedUpdateFailed(const itk::EventObject &)
{
ThreadedUpdateFailed();
-
- m_ParameterListMutex->Lock();
- m_ThreadID = -1; // tested before starting
- m_ParameterListMutex->Unlock();
m_ThreadParameters.m_Algorithm = nullptr; // delete
}
void NonBlockingAlgorithm::ThreadedUpdateFailed()
{
// notify observers that something went wrong
InvokeEvent(ProcessingError(this));
}
} // namespace
diff --git a/Modules/AlgorithmsExt/src/mitkPlaneFit.cpp b/Modules/AlgorithmsExt/src/mitkPlaneFit.cpp
index 3bf7846bc8..dd617de6f5 100644
--- a/Modules/AlgorithmsExt/src/mitkPlaneFit.cpp
+++ b/Modules/AlgorithmsExt/src/mitkPlaneFit.cpp
@@ -1,193 +1,192 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkPlaneFit.h"
#include "mitkGeometryData.h"
#include "mitkPlaneGeometry.h"
#include <mitkProportionalTimeGeometry.h>
-#include <vcl_iostream.h>
#include <vnl/algo/vnl_svd.h>
mitk::PlaneFit::PlaneFit() : m_PointSet(nullptr)
{
m_TimeGeometry = mitk::ProportionalTimeGeometry::New();
}
mitk::PlaneFit::~PlaneFit()
{
}
void mitk::PlaneFit::GenerateOutputInformation()
{
mitk::PointSet::ConstPointer input = this->GetInput();
mitk::GeometryData::Pointer output = this->GetOutput();
itkDebugMacro(<< "GenerateOutputInformation()");
if (input.IsNull())
return;
if (m_PointSet == nullptr)
{
return;
}
bool update = false;
if (output->GetGeometry() == nullptr || output->GetTimeGeometry() == nullptr)
update = true;
if ((!update) && (output->GetTimeGeometry()->CountTimeSteps() != input->GetTimeGeometry()->CountTimeSteps()))
update = true;
if (update)
{
mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New();
ProportionalTimeGeometry::Pointer timeGeometry =
dynamic_cast<ProportionalTimeGeometry *>(m_TimeGeometry.GetPointer());
timeGeometry->Initialize(planeGeometry, m_PointSet->GetPointSetSeriesSize());
// m_TimeGeometry->InitializeEvenlyTimed(
// planeGeometry, m_PointSet->GetPointSetSeriesSize() );
TimeStepType timeStep;
for (timeStep = 0; (timeStep < m_PointSet->GetPointSetSeriesSize()) && (timeStep < m_Planes.size()); ++timeStep)
{
timeGeometry->SetTimeStepGeometry(m_Planes[timeStep], timeStep);
}
output->SetTimeGeometry(m_TimeGeometry);
}
}
void mitk::PlaneFit::GenerateData()
{
unsigned int t;
for (t = 0; t < m_PointSet->GetPointSetSeriesSize(); ++t)
{
// check number of data points - less then 3points isn't enough
if (m_PointSet->GetSize(t) >= 3)
{
this->CalculateCentroid(t);
this->ProcessPointSet(t);
this->InitializePlane(t);
}
}
}
void mitk::PlaneFit::SetInput(const mitk::PointSet *pointSet)
{
// Process object is not const-correct so the const_cast is required here
this->ProcessObject::SetNthInput(0, const_cast<mitk::PointSet *>(pointSet));
m_PointSet = pointSet;
unsigned int pointSetSize = pointSet->GetPointSetSeriesSize();
m_Planes.resize(pointSetSize);
m_Centroids.resize(pointSetSize);
m_PlaneVectors.resize(pointSetSize);
unsigned int t;
for (t = 0; t < pointSetSize; ++t)
{
m_Planes[t] = mitk::PlaneGeometry::New();
}
}
const mitk::PointSet *mitk::PlaneFit::GetInput()
{
if (this->GetNumberOfInputs() < 1)
{
return nullptr;
}
return static_cast<const mitk::PointSet *>(this->ProcessObject::GetInput(0));
}
void mitk::PlaneFit::CalculateCentroid(int t)
{
if (m_PointSet == nullptr)
return;
int ps_total = m_PointSet->GetSize(t);
m_Centroids[t][0] = m_Centroids[t][1] = m_Centroids[t][2] = 0.0;
for (int i = 0; i < ps_total; i++)
{
mitk::Point3D p3d = m_PointSet->GetPoint(i, t);
m_Centroids[t][0] += p3d[0];
m_Centroids[t][1] += p3d[1];
m_Centroids[t][2] += p3d[2];
}
// calculation of centroid
m_Centroids[t][0] /= ps_total;
m_Centroids[t][1] /= ps_total;
m_Centroids[t][2] /= ps_total;
}
void mitk::PlaneFit::ProcessPointSet(int t)
{
if (m_PointSet == nullptr)
return;
// int matrix with POINTS x (X,Y,Z)
vnl_matrix<mitk::ScalarType> dataM(m_PointSet->GetSize(t), 3);
int ps_total = m_PointSet->GetSize(t);
for (int i = 0; i < ps_total; i++)
{
mitk::Point3D p3d = m_PointSet->GetPoint(i, t);
dataM[i][0] = p3d[0] - m_Centroids[t][0];
dataM[i][1] = p3d[1] - m_Centroids[t][1];
dataM[i][2] = p3d[2] - m_Centroids[t][2];
}
// process the SVD (singular value decomposition) from ITK
// the vector will be orderd descending
vnl_svd<mitk::ScalarType> svd(dataM, 0.0);
// calculate the SVD of A
vnl_vector<mitk::ScalarType> v = svd.nullvector();
// Avoid erratic normal sign switching when the plane changes minimally
// by negating the vector for negative x values.
if (v[0] < 0)
{
v = -v;
}
m_PlaneVectors[t][0] = v[0];
m_PlaneVectors[t][1] = v[1];
m_PlaneVectors[t][2] = v[2];
}
mitk::PlaneGeometry::Pointer mitk::PlaneFit::GetPlaneGeometry(int t)
{
return m_Planes[t];
}
const mitk::Vector3D &mitk::PlaneFit::GetPlaneNormal(int t) const
{
return m_PlaneVectors[t];
}
const mitk::Point3D &mitk::PlaneFit::GetCentroid(int t) const
{
return m_Centroids[t];
}
void mitk::PlaneFit::InitializePlane(int t)
{
m_Planes[t]->InitializePlane(m_Centroids[t], m_PlaneVectors[t]);
}
diff --git a/Modules/AlgorithmsExt/src/mitkSimpleHistogram.cpp b/Modules/AlgorithmsExt/src/mitkSimpleHistogram.cpp
index 3615e69a1a..1174ac545d 100644
--- a/Modules/AlgorithmsExt/src/mitkSimpleHistogram.cpp
+++ b/Modules/AlgorithmsExt/src/mitkSimpleHistogram.cpp
@@ -1,311 +1,311 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkSimpleHistogram.h"
#include "mitkImageReadAccessor.h"
#include "mitkSimpleUnstructuredGridHistogram.h"
#include "mitkUnstructuredGrid.h"
namespace mitk
{
void SimpleImageHistogram::ComputeFromBaseData(BaseData *src)
{
valid = false;
// check if input is valid
if (src == nullptr)
return;
auto *source = dynamic_cast<Image *>(src);
if (source == nullptr)
return;
else if (source->IsEmpty())
return;
// dummy histogram
{
min = 0;
max = 1;
first = 0;
last = 1;
}
{
int typInt = 0;
{
- const int typ = source->GetPixelType().GetComponentType();
- if (typ == itk::ImageIOBase::UCHAR)
+ auto typ = source->GetPixelType().GetComponentType();
+ if (typ == itk::IOComponentEnum::UCHAR)
typInt = 0;
- else if (typ == itk::ImageIOBase::CHAR)
+ else if (typ == itk::IOComponentEnum::CHAR)
typInt = 1;
- else if (typ == itk::ImageIOBase::USHORT)
+ else if (typ == itk::IOComponentEnum::USHORT)
typInt = 2;
- else if (typ == itk::ImageIOBase::SHORT)
+ else if (typ == itk::IOComponentEnum::SHORT)
typInt = 3;
- else if (typ == itk::ImageIOBase::INT)
+ else if (typ == itk::IOComponentEnum::INT)
typInt = 4;
- else if (typ == itk::ImageIOBase::UINT)
+ else if (typ == itk::IOComponentEnum::UINT)
typInt = 5;
- else if (typ == itk::ImageIOBase::LONG)
+ else if (typ == itk::IOComponentEnum::LONG)
typInt = 6;
- else if (typ == itk::ImageIOBase::ULONG)
+ else if (typ == itk::IOComponentEnum::ULONG)
typInt = 7;
- else if (typ == itk::ImageIOBase::FLOAT)
+ else if (typ == itk::IOComponentEnum::FLOAT)
typInt = 8;
- else if (typ == itk::ImageIOBase::DOUBLE)
+ else if (typ == itk::IOComponentEnum::DOUBLE)
typInt = 9;
else
{
MITK_WARN << "Pixel type not supported by SimpleImageHistogram";
return;
}
}
first = -32768;
last = 65535; // support at least full signed and unsigned short range
if (histogram)
delete histogram;
histogram = new CountType[last - first + 1];
memset(histogram, 0, sizeof(CountType) * (last - first + 1));
highest = 0;
max = first - 1;
min = last + 1;
unsigned int num = 1;
for (unsigned int r = 0; r < source->GetDimension(); r++)
num *= source->GetDimension(r);
// MITK_INFO << "building histogramm of integer image: 0=" << source->GetDimension(0) << " 1=" <<
// source->GetDimension(1) << " 2=" << source->GetDimension(2) << " 3=" << source->GetDimension(3);
ImageReadAccessor sourceAcc(source);
const void *src = sourceAcc.GetData();
do
{
int value = 0;
switch (typInt)
{
case 0:
{
auto *t = (unsigned char *)src;
value = *t++;
src = (void *)t;
}
break;
case 1:
{
auto *t = (signed char *)src;
value = *t++;
src = (void *)t;
}
break;
case 2:
{
auto *t = (unsigned short *)src;
value = *t++;
src = (void *)t;
}
break;
case 3:
{
auto *t = (signed short *)src;
value = *t++;
src = (void *)t;
}
break;
case 4:
{
auto *t = (signed int *)src;
value = *t++;
src = (void *)t;
}
break;
case 5:
{
auto *t = (unsigned int *)src;
value = *t++;
src = (void *)t;
}
break;
case 6:
{
auto *t = (signed long *)src;
value = *t++;
src = (void *)t;
}
break;
case 7:
{
auto *t = (unsigned long *)src;
value = *t++;
src = (void *)t;
}
break;
case 8:
{
auto *t = (float *)src;
value = *t++;
src = (void *)t;
}
break;
case 9:
{
auto *t = (double *)src;
value = *t++;
src = (void *)t;
}
break;
}
if (value >= first && value <= last)
{
if (value < min)
min = value;
if (value > max)
max = value;
CountType tmp = ++histogram[value - first];
if (tmp > highest)
highest = tmp;
}
} while (--num);
MITK_INFO << "histogramm computed: min=" << min << " max=" << max << " highestBin=" << highest
<< " samples=" << num;
}
invLogHighest = 1.0 / log(double(highest));
valid = true;
}
bool SimpleImageHistogram::GetValid() { return valid; }
float SimpleImageHistogram::GetRelativeBin(double left, double right) const
{
if (!valid)
return 0.0f;
int iLeft = floorf(left);
int iRight = ceilf(right);
/*
double sum = 0;
for( int r = 0 ; r < 256 ; r++)
{
int pos = left + (right-left) * r/255.0;
int posInArray = floorf(pos+0.5f) - first;
sum += float(log(double(histogram[posInArray])));
}
sum /= 256.0;
return float(sum*invLogHighest);
*/
CountType maximum = 0;
for (int i = iLeft; i <= iRight; i++)
{
int posInArray = i - first;
if (histogram[posInArray] > maximum)
maximum = histogram[posInArray];
}
return float(log(double(maximum)) * invLogHighest);
}
class ImageHistogramCacheElement : public SimpleHistogramCache::Element
{
public:
void ComputeFromBaseData(BaseData *baseData) override { histogram.ComputeFromBaseData(baseData); }
SimpleHistogram *GetHistogram() override { return &histogram; }
SimpleImageHistogram histogram;
};
class UnstructuredGridHistogramCacheElement : public SimpleHistogramCache::Element
{
public:
void ComputeFromBaseData(BaseData *baseData) override { histogram.ComputeFromBaseData(baseData); }
SimpleHistogram *GetHistogram() override { return &histogram; }
SimpleUnstructuredGridHistogram histogram;
};
SimpleHistogram *SimpleHistogramCache::operator[](BaseData::Pointer sp_BaseData)
{
BaseData *p_BaseData = sp_BaseData.GetPointer();
if (!p_BaseData)
{
MITK_WARN << "SimpleHistogramCache::operator[] with null base data called";
return nullptr;
}
Element *elementToUpdate = nullptr;
bool first = true;
for (auto iter = cache.begin(); iter != cache.end(); iter++)
{
Element *e = *iter;
BaseData *p_tmp = e->baseData.Lock();
if (p_tmp == p_BaseData)
{
if (!first)
{
cache.erase(iter);
cache.push_front(e);
}
if (p_BaseData->GetMTime() > e->m_LastUpdateTime.GetMTime())
{
elementToUpdate = e;
goto recomputeElement;
}
// MITK_INFO << "using a cached histogram";
return e->GetHistogram();
}
first = false;
}
if (dynamic_cast<Image *>(p_BaseData))
{
elementToUpdate = new ImageHistogramCacheElement();
}
else if (dynamic_cast<UnstructuredGrid *>(p_BaseData))
{
elementToUpdate = new UnstructuredGridHistogramCacheElement();
}
else
{
MITK_WARN << "not supported: " << p_BaseData->GetNameOfClass();
}
elementToUpdate->baseData = p_BaseData;
cache.push_front(elementToUpdate);
TrimCache();
recomputeElement:
// MITK_INFO << "computing a new histogram";
elementToUpdate->ComputeFromBaseData(p_BaseData);
elementToUpdate->m_LastUpdateTime.Modified();
return elementToUpdate->GetHistogram();
}
SimpleHistogramCache::Element::~Element() {}
}
diff --git a/Modules/AlgorithmsExt/src/mitkWeightedPointTransform.cpp b/Modules/AlgorithmsExt/src/mitkWeightedPointTransform.cpp
index ce6f229ea5..d1ceab8092 100644
--- a/Modules/AlgorithmsExt/src/mitkWeightedPointTransform.cpp
+++ b/Modules/AlgorithmsExt/src/mitkWeightedPointTransform.cpp
@@ -1,459 +1,459 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// MITK
#include "mitkWeightedPointTransform.h"
#include "mitkAnisotropicRegistrationCommon.h"
#include <vtkLandmarkTransform.h>
#include <vtkMatrix4x4.h>
#include <vtkPoints.h>
typedef itk::Matrix<double, 3, 3> Matrix3x3;
typedef std::vector<Matrix3x3> Matrix3x3List;
///////////////////////////////////////////////
// forward declarations of private functions
///////////////////////////////////////////////
static double ComputeWeightedFRE(vtkPoints *X,
vtkPoints *Y,
const Matrix3x3List &CovarianceMatricesMoving,
const Matrix3x3List &CovarianceMatricesFixed,
double FRENormalizationFactor,
Matrix3x3List &WeightMatrices,
const Matrix3x3 &rotation,
const itk::Vector<double, 3> &translation);
static void calculateWeightMatrices(const Matrix3x3List &X,
const Matrix3x3List &Y,
Matrix3x3List &result,
const Matrix3x3 &rotation);
static void IsotropicRegistration(vtkPoints *X,
vtkPoints *Y,
vtkLandmarkTransform *landmarkTransform,
Matrix3x3 &rotation,
itk::Vector<double, 3> &translation);
mitk::WeightedPointTransform::WeightedPointTransform()
: m_Threshold(1.0e-4),
m_MaxIterations(1000),
m_Iterations(-1),
m_FRE(-1.0),
m_FRENormalizationFactor(1.0),
m_LandmarkTransform(vtkSmartPointer<vtkLandmarkTransform>::New())
{
}
mitk::WeightedPointTransform::~WeightedPointTransform()
{
m_FixedPointSet = nullptr;
m_MovingPointSet = nullptr;
m_LandmarkTransform = nullptr;
}
void mitk::WeightedPointTransform::ComputeTransformation()
{
WeightedPointRegister(m_MovingPointSet,
m_FixedPointSet,
m_CovarianceMatricesMoving,
m_CovarianceMatricesFixed,
m_Threshold,
m_MaxIterations,
m_Rotation,
m_Translation,
m_FRE,
m_Iterations);
}
// computes the weightmatrix with 2 covariance matrices
// and a given transformation
void calculateWeightMatrices(const Matrix3x3List &X,
const Matrix3x3List &Y,
Matrix3x3List &result,
const Matrix3x3 &rotation)
{
const vnl_matrix_fixed<double, 3, 3> rotation_T = rotation.GetTranspose();
#pragma omp parallel for
for (int i = 0; i < static_cast<int>(X.size()); ++i)
{
const Matrix3x3 w = rotation * X[i] * rotation_T;
result[i] = mitk::AnisotropicRegistrationCommon::CalculateWeightMatrix(w, Y[i]);
}
}
// computes the weighted fiducial registration error
double ComputeWeightedFRE(vtkPoints *X,
vtkPoints *Y,
const Matrix3x3List &CovarianceMatricesMoving,
const Matrix3x3List &CovarianceMatricesFixed,
double FRENormalizationFactor,
Matrix3x3List &WeightMatrices,
const Matrix3x3 &rotation,
const itk::Vector<double, 3> &translation)
{
double FRE = 0;
// compute weighting matrices
calculateWeightMatrices(CovarianceMatricesMoving, CovarianceMatricesFixed, WeightMatrices, rotation);
#pragma omp parallel for
for (int i = 0; i < static_cast<int>(WeightMatrices.size()); ++i)
{
// convert to itk data types (nessecary since itk 4 migration)
itk::Vector<double, 3> converted_MovingPoint;
double point[3];
X->GetPoint(i, point);
converted_MovingPoint[0] = point[0];
converted_MovingPoint[1] = point[1];
converted_MovingPoint[2] = point[2];
// transform point
itk::Vector<double, 3> p = rotation * converted_MovingPoint + translation;
Y->GetPoint(i, point);
p[0] -= point[0];
p[1] -= point[1];
p[2] -= point[2];
// do calculation
const itk::Vector<double, 3> D = WeightMatrices.at(i) * p;
#pragma omp critical
FRE += (D[0] * D[0] + D[1] * D[1] + D[2] * D[2]);
}
FRE /= WeightMatrices.size();
FRE = FRENormalizationFactor * sqrt(FRE);
return FRE;
}
// registers two pointsets with an isotropic landmark transform
void IsotropicRegistration(vtkPoints *X,
vtkPoints *Y,
vtkLandmarkTransform *landmarkTransform,
Matrix3x3 &rotation,
itk::Vector<double, 3> &translation)
{
landmarkTransform->SetSourceLandmarks(X);
landmarkTransform->SetTargetLandmarks(Y);
landmarkTransform->SetModeToRigidBody();
landmarkTransform->Modified();
landmarkTransform->Update();
vtkMatrix4x4 *m = landmarkTransform->GetMatrix();
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j)
rotation[i][j] = m->GetElement(i, j);
translation[0] = m->GetElement(0, 3);
translation[1] = m->GetElement(1, 3);
translation[2] = m->GetElement(2, 3);
}
void mitk::WeightedPointTransform::C_maker(vtkPoints *X,
const WeightMatrixList &W,
itk::VariableSizeMatrix<double> &returnValue)
{
#pragma omp parallel for
for (int i = 0; i < X->GetNumberOfPoints(); ++i)
{
unsigned int index = 3u * i;
double point[3];
X->GetPoint(i, point);
for (int j = 0; j < 3; ++j)
{
returnValue[index][0] = -W.at(i)[j][1] * point[2] + W.at(i)[j][2] * point[1];
returnValue[index][1] = W.at(i)[j][0] * point[2] - W.at(i)[j][2] * point[0];
returnValue[index][2] = -W.at(i)[j][0] * point[1] + W.at(i)[j][1] * point[0];
returnValue[index][3] = W.at(i)[j][0];
returnValue[index][4] = W.at(i)[j][1];
returnValue[index][5] = W.at(i)[j][2];
index += 1;
}
}
}
void mitk::WeightedPointTransform::E_maker(vtkPoints *X,
vtkPoints *Y,
const WeightMatrixList &W,
vnl_vector<double> &returnValue)
{
#pragma omp parallel for
for (int i = 0; i < X->GetNumberOfPoints(); ++i)
{
unsigned int index = 3u * i;
double pX[3];
double pY[3];
Matrix3x3 M;
X->GetPoint(i, pX);
Y->GetPoint(i, pY);
M[0][0] = pY[0] - pX[0];
M[0][1] = pY[1] - pX[1];
M[0][2] = pY[2] - pX[2];
M[1][0] = M[0][0];
M[1][1] = M[0][1];
M[1][2] = M[0][2];
M[2][0] = M[0][0];
M[2][1] = M[0][1];
M[2][2] = M[0][2];
for (unsigned int j = 0; j < 3; ++j)
{
returnValue[index + j] = W.at(i)[j][0] * M[j][0] + W.at(i)[j][1] * M[j][1] + W.at(i)[j][2] * M[j][2];
}
}
}
void mitk::WeightedPointTransform::WeightedPointRegister(vtkPoints *X,
vtkPoints *Y,
const CovarianceMatrixList &Sigma_X,
const CovarianceMatrixList &Sigma_Y,
double Threshold,
int MaxIterations,
Rotation &TransformationR,
Translation &TransformationT,
double &FRE,
int &n)
{
double FRE_identity = 0.0;
double FRE_isotropic_weighted = 0.0;
double initialFRE = 0.0;
// set config_change to infinite (max double) at start
double config_change = std::numeric_limits<double>::max();
Rotation initial_TransformationR;
initial_TransformationR.SetIdentity();
Translation initial_TransformationT;
initial_TransformationT.Fill(0.0);
// Weightmatrices
Matrix3x3List W;
vtkPoints *X_transformed = vtkPoints::New();
vtkPoints *X_transformedNew = vtkPoints::New();
vnl_vector<double> oldq;
itk::VariableSizeMatrix<double> iA;
vnl_vector<double> iB;
// initialize memory
W.resize(X->GetNumberOfPoints());
X_transformed->SetNumberOfPoints(X->GetNumberOfPoints());
X_transformedNew->SetNumberOfPoints(X->GetNumberOfPoints());
iA.SetSize(3u * X->GetNumberOfPoints(), 6u);
iB.set_size(3u * X->GetNumberOfPoints());
// calculate FRE_0 with identity transform
FRE_identity = ComputeWeightedFRE(
X, Y, Sigma_X, Sigma_Y, m_FRENormalizationFactor, W, initial_TransformationR, initial_TransformationT);
MITK_DEBUG << "FRE for identity transform: " << FRE_identity;
// compute isotropic transformation as initial estimate
IsotropicRegistration(X, Y, m_LandmarkTransform, initial_TransformationR, initial_TransformationT);
// result of unweighted registration algorithm
TransformationR = initial_TransformationR;
TransformationT = initial_TransformationT;
// calculate FRE_0 with isotropic transform
FRE_isotropic_weighted =
ComputeWeightedFRE(X, Y, Sigma_X, Sigma_Y, m_FRENormalizationFactor, W, TransformationR, TransformationT);
MITK_DEBUG << "FRE for transform obtained with unweighted registration: " << FRE_isotropic_weighted;
// if R,t is worse than the identity, use the identity as initial transform
if (FRE_isotropic_weighted < FRE_identity)
{
initialFRE = FRE_isotropic_weighted;
}
else
{
initialFRE = FRE_identity;
TransformationR.SetIdentity(); // set rotation to identity element
TransformationT.Fill(0.0); // set translation to identity element
initial_TransformationR.SetIdentity();
initial_TransformationT.Fill(0.0);
}
// apply transform to moving set:
mitk::AnisotropicRegistrationCommon::TransformPoints(X, X_transformed, TransformationR, TransformationT);
// start with iteration 0
n = 0;
do
{
n++;
calculateWeightMatrices(Sigma_X, Sigma_Y, W, TransformationR);
//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
// PROBLEM: no square matrix but the backslash operator in matlab does solve the system anyway. How to convert this
// to C++?
// good descriptons to the "backslash"-operator (in german):
// http://www.tm-mathe.de/Themen/html/matlab__zauberstab__backslash-.html
// http://www.tm-mathe.de/Themen/html/matlab__matrix-division__vorsi.html#HoheMatrixA
//
// current method: treat the problem as a minimization problem, because this is what the
// "backslash"-operator also does with "high" matrices.
// (and we will have those matrices in most cases)
C_maker(X_transformed, W, iA);
E_maker(X_transformed, Y, W, iB);
vnl_matrix_inverse<double> myInverse(iA.GetVnlMatrix());
vnl_vector<double> q = myInverse.pinverse(iB.size()) * iB;
//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
if (n > 1)
q = (q + oldq) / 2;
oldq = q;
itk::Vector<double, 3> delta_t;
delta_t[0] = q[3];
delta_t[1] = q[4];
delta_t[2] = q[5];
Matrix3x3 delta_theta;
delta_theta[0][0] = 1;
delta_theta[0][1] = -q[2];
delta_theta[0][2] = q[1];
delta_theta[1][0] = q[2];
delta_theta[1][1] = 1;
delta_theta[1][2] = -q[0];
delta_theta[2][0] = -q[1];
delta_theta[2][1] = q[0];
delta_theta[2][2] = 1;
- vnl_svd<double> svd_delta_theta(delta_theta.GetVnlMatrix());
+ vnl_svd<double> svd_delta_theta(delta_theta.GetVnlMatrix().as_ref());
// convert vnl matrices to itk matrices...
Matrix3x3 U;
Matrix3x3 V;
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 3; ++j)
{
U[i][j] = svd_delta_theta.U()[i][j];
V[i][j] = svd_delta_theta.V()[i][j];
}
}
Matrix3x3 delta_R = U * V.GetTranspose();
// update rotation
TransformationR = delta_R * TransformationR;
// update translation
TransformationT = delta_R * TransformationT + delta_t;
// update moving points
mitk::AnisotropicRegistrationCommon::TransformPoints(X, X_transformedNew, TransformationR, TransformationT);
// calculate config change
config_change = CalculateConfigChange(X_transformed, X_transformedNew);
// swap the pointers the old set for the next iteration is
// the new set of the last iteration
vtkPoints *tmp = X_transformed;
X_transformed = X_transformedNew;
X_transformedNew = tmp;
} while (config_change > Threshold && n < MaxIterations);
// calculate FRE with current transform
FRE = ComputeWeightedFRE(X, Y, Sigma_X, Sigma_Y, m_FRENormalizationFactor, W, TransformationR, TransformationT);
MITK_DEBUG << "FRE after algorithm (prior to check with initial): " << FRE;
// compare with FRE_initial
if (initialFRE < FRE)
{
MITK_WARN << "FRE did not improve in anisotropic point registration function";
TransformationR = initial_TransformationR;
TransformationT = initial_TransformationT;
FRE = initialFRE;
}
MITK_DEBUG << "FRE final: " << FRE;
X_transformed->Delete();
X_transformedNew->Delete();
}
void mitk::WeightedPointTransform::SetMovingPointSet(vtkSmartPointer<vtkPoints> p)
{
m_MovingPointSet = p;
}
void mitk::WeightedPointTransform::SetCovarianceMatricesMoving(const CovarianceMatrixList &matrices)
{
m_CovarianceMatricesMoving = matrices;
}
void mitk::WeightedPointTransform::SetFixedPointSet(vtkSmartPointer<vtkPoints> p)
{
m_FixedPointSet = p;
}
void mitk::WeightedPointTransform::SetCovarianceMatricesFixed(const CovarianceMatrixList &matrices)
{
m_CovarianceMatricesFixed = matrices;
}
double mitk::WeightedPointTransform::CalculateConfigChange(vtkPoints *X, vtkPoints *X_new)
{
double sum[3] = {0.0, 0.0, 0.0};
double mean[3] = {0.0, 0.0, 0.0};
double pX[3] = {0.0, 0.0, 0.0};
double pX_new[3] = {0.0, 0.0, 0.0};
// compute mean of the old point set and the first sum
for (vtkIdType i = 0; i < X->GetNumberOfPoints(); ++i)
{
X->GetPoint(i, pX);
X_new->GetPoint(i, pX_new);
// first sum
sum[0] += (pX_new[0] - pX[0]) * (pX_new[0] - pX[0]);
sum[1] += (pX_new[1] - pX[1]) * (pX_new[1] - pX[1]);
sum[2] += (pX_new[2] - pX[2]) * (pX_new[2] - pX[2]);
// mean
mean[0] += pX[0];
mean[1] += pX[1];
mean[2] += pX[2];
}
mean[0] /= X->GetNumberOfPoints();
mean[1] /= X->GetNumberOfPoints();
mean[2] /= X->GetNumberOfPoints();
const double us = sum[0] + sum[1] + sum[2];
// reset sum
sum[0] = sum[1] = sum[2] = 0.0;
for (vtkIdType i = 0; i < X->GetNumberOfPoints(); ++i)
{
X->GetPoint(i, pX);
sum[0] += (pX[0] - mean[0]) * (pX[0] - mean[0]);
sum[1] += (pX[1] - mean[1]) * (pX[1] - mean[1]);
sum[2] += (pX[2] - mean[2]) * (pX[2] - mean[2]);
}
const double ls = sum[0] + sum[1] + sum[2];
return sqrt(us / ls);
}
diff --git a/Modules/BasicImageProcessing/src/mitkTransformationOperation.cpp b/Modules/BasicImageProcessing/src/mitkTransformationOperation.cpp
index 87269991c4..29dfb3d4d7 100644
--- a/Modules/BasicImageProcessing/src/mitkTransformationOperation.cpp
+++ b/Modules/BasicImageProcessing/src/mitkTransformationOperation.cpp
@@ -1,500 +1,500 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkTransformationOperation.h"
#include <mitkImage.h>
#include <mitkImageAccessByItk.h>
#include <mitkImageCast.h>
#include <itkImage.h>
#include <itkRecursiveMultiResolutionPyramidImageFilter.h>
#include <itkLaplacianRecursiveGaussianImageFilter.h>
// Wavelet
#include <itkWaveletFrequencyForward.h>
#include <itkWaveletFrequencyFilterBankGenerator.h>
#include <itkHeldIsotropicWavelet.h>
#include <itkVowIsotropicWavelet.h>
#include <itkSimoncelliIsotropicWavelet.h>
#include <itkShannonIsotropicWavelet.h>
#include <itkForwardFFTImageFilter.h>
#include <itkInverseFFTImageFilter.h>
-#include <itkFFTPadPositiveIndexImageFilter.h>
+#include <itkFFTPadImageFilter.h>
#include "itkZeroFluxNeumannBoundaryCondition.h"
#include "itkPeriodicBoundaryCondition.h"
#include "itkConstantBoundaryCondition.h"
//#include <itkComplexToRealImageFilter.h>
#include "itkCastImageFilter.h"
#include "itkUnaryFunctorImageFilter.h"
#include <mitkImageMappingHelper.h>
#include <mitkMAPAlgorithmHelper.h>
#include <itkImageDuplicator.h>
namespace mitk
{
namespace Functor
{
template< class TInput>
class ThresholdValue
{
public:
ThresholdValue() {};
~ThresholdValue() {};
bool operator!=(const ThresholdValue &) const
{
return false;
}
bool operator==(const ThresholdValue & other) const
{
return !(*this != other);
}
inline unsigned short operator()(const TInput & A) const
{
if (A < value)
return 0;
else
return 1;
}
double value = 0.0;
};
template< class TInput, class TOutput>
class RoundValue
{
public:
RoundValue() {};
~RoundValue() {};
bool operator!=(const RoundValue &) const
{
return false;
}
bool operator==(const RoundValue & other) const
{
return !(*this != other);
}
inline TOutput operator()(const TInput & A) const
{
return std::round(A);
}
};
}
}
template<typename TPixel, unsigned int VImageDimension>
static void ExecuteMultiResolution(itk::Image<TPixel, VImageDimension>* image, unsigned int numberOfLevels, bool outputAsDouble, std::vector<mitk::Image::Pointer> &resultImages)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::Image<double, VImageDimension> DoubleOutputType;
typedef itk::RecursiveMultiResolutionPyramidImageFilter<ImageType, ImageType> ImageTypeFilterType;
typedef itk::RecursiveMultiResolutionPyramidImageFilter<ImageType, DoubleOutputType> DoubleTypeFilterType;
if (outputAsDouble)
{
typename DoubleTypeFilterType::Pointer recursiveMultiResolutionPyramidImageFilter = DoubleTypeFilterType::New();
recursiveMultiResolutionPyramidImageFilter->SetInput(image);
recursiveMultiResolutionPyramidImageFilter->SetNumberOfLevels(numberOfLevels);
recursiveMultiResolutionPyramidImageFilter->Update();
// This outputs the levels (0 is the lowest resolution)
for (unsigned int i = 0; i < numberOfLevels; ++i)
{
mitk::Image::Pointer outputImage = mitk::Image::New();
CastToMitkImage(recursiveMultiResolutionPyramidImageFilter->GetOutput(i), outputImage);
resultImages.push_back(outputImage);
}
}
else {
typename ImageTypeFilterType::Pointer recursiveMultiResolutionPyramidImageFilter = ImageTypeFilterType::New();
recursiveMultiResolutionPyramidImageFilter->SetInput(image);
recursiveMultiResolutionPyramidImageFilter->SetNumberOfLevels(numberOfLevels);
recursiveMultiResolutionPyramidImageFilter->Update();
// This outputs the levels (0 is the lowest resolution)
for (unsigned int i = 0; i < numberOfLevels; ++i)
{
mitk::Image::Pointer outputImage = mitk::Image::New();
CastToMitkImage(recursiveMultiResolutionPyramidImageFilter->GetOutput(i), outputImage);
resultImages.push_back(outputImage);
}
}
}
std::vector<mitk::Image::Pointer> mitk::TransformationOperation::MultiResolution(Image::Pointer & image, unsigned int numberOfLevels, bool outputAsDouble)
{
std::vector<Image::Pointer> resultImages;
AccessByItk_n(image, ExecuteMultiResolution, (numberOfLevels, outputAsDouble, resultImages));
return resultImages;
}
template<typename TPixel, unsigned int VImageDimension>
static void ExecuteLaplacianOfGaussian(itk::Image<TPixel, VImageDimension>* image, double sigma, bool outputAsDouble, mitk::Image::Pointer &resultImage)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::Image<double, VImageDimension> DoubleOutputType;
typedef itk::LaplacianRecursiveGaussianImageFilter<ImageType, ImageType> ImageTypeFilterType;
typedef itk::LaplacianRecursiveGaussianImageFilter<ImageType, DoubleOutputType> DoubleTypeFilterType;
if (outputAsDouble)
{
typename DoubleTypeFilterType::Pointer filter = DoubleTypeFilterType::New();
filter->SetInput(image);
filter->SetSigma(sigma);
filter->Update();
CastToMitkImage(filter->GetOutput(), resultImage);
}
else {
typename ImageTypeFilterType::Pointer filter = ImageTypeFilterType::New();
filter->SetInput(image);
filter->SetSigma(sigma);
filter->Update();
CastToMitkImage(filter->GetOutput(), resultImage);
}
}
mitk::Image::Pointer mitk::TransformationOperation::LaplacianOfGaussian(Image::Pointer & image, double sigma, bool outputAsDouble)
{
Image::Pointer resultImage;
AccessByItk_n(image, ExecuteLaplacianOfGaussian, (sigma, outputAsDouble, resultImage));
return resultImage;
}
template<typename TInputPixel, typename TOutputPixel, unsigned int VImageDimension, typename TWaveletFunction >
static void ExecuteSpecificWaveletTransformation(itk::Image<TInputPixel, VImageDimension>* image, unsigned int numberOfLevels, unsigned int numberOfBands, mitk::BorderCondition condition, std::vector<mitk::Image::Pointer> &resultImages)
{
const unsigned int Dimension = VImageDimension;
typedef TInputPixel PixelType;
typedef TOutputPixel OutputPixelType;
typedef itk::Image< PixelType, Dimension > ImageType;
typedef itk::Image< double, Dimension > DoubleImageType;
typedef itk::Image< OutputPixelType, Dimension > OutputImageType;
typedef itk::CastImageFilter< ImageType, DoubleImageType > CastFilterType;
- typedef itk::FFTPadPositiveIndexImageFilter< DoubleImageType > FFTPadType;
+ typedef itk::FFTPadImageFilter< DoubleImageType > FFTPadType;
typedef itk::ForwardFFTImageFilter< DoubleImageType, itk::Image< std::complex<double>, Dimension> > FFTFilterType;
typedef typename FFTFilterType::OutputImageType ComplexImageType;
typedef TWaveletFunction WaveletFunctionType;
typedef itk::WaveletFrequencyFilterBankGenerator< ComplexImageType, WaveletFunctionType > WaveletFilterBankType;
typedef itk::WaveletFrequencyForward< ComplexImageType, ComplexImageType, WaveletFilterBankType > ForwardWaveletType;
typedef itk::InverseFFTImageFilter< ComplexImageType, OutputImageType > InverseFFTFilterType;
// Convert input parameter
unsigned int highSubBands = numberOfBands; //inputBands;
unsigned int levels = numberOfLevels;
// Perform FFT on input image
typename CastFilterType::Pointer castFilter = CastFilterType::New();
castFilter->SetInput(image);
// Pad Image so it fits the expect
typename FFTPadType::Pointer fftpad = FFTPadType::New();
fftpad->SetSizeGreatestPrimeFactor(4);
itk::ConstantBoundaryCondition< DoubleImageType > constantBoundaryCondition;
itk::PeriodicBoundaryCondition< DoubleImageType > periodicBoundaryCondition;
itk::ZeroFluxNeumannBoundaryCondition< DoubleImageType > zeroFluxNeumannBoundaryCondition;
switch (condition)
{
case mitk::BorderCondition::Constant:
fftpad->SetBoundaryCondition(&constantBoundaryCondition);
break;
case mitk::BorderCondition::Periodic:
fftpad->SetBoundaryCondition(&periodicBoundaryCondition);
break;
case mitk::BorderCondition::ZeroFluxNeumann:
fftpad->SetBoundaryCondition(&zeroFluxNeumannBoundaryCondition);
break;
default:
break;
}
fftpad->SetInput(castFilter->GetOutput());
typename FFTFilterType::Pointer fftFilter = FFTFilterType::New();
fftFilter->SetInput(fftpad->GetOutput());
// Calculate forward transformation
typename ForwardWaveletType::Pointer forwardWavelet = ForwardWaveletType::New();
forwardWavelet->SetHighPassSubBands(highSubBands);
forwardWavelet->SetLevels(levels);
forwardWavelet->SetInput(fftFilter->GetOutput());
forwardWavelet->Update();
// Obtain target spacing, size and origin
typename ComplexImageType::SpacingType inputSpacing;
for (unsigned int i = 0; i < Dimension; ++i)
{
inputSpacing[i] = image->GetLargestPossibleRegion().GetSize()[i];
}
typename ComplexImageType::SpacingType expectedSpacing = inputSpacing;
typename ComplexImageType::PointType inputOrigin = image->GetOrigin();
typename ComplexImageType::PointType expectedOrigin = inputOrigin;
typename ComplexImageType::SizeType inputSize = fftFilter->GetOutput()->GetLargestPossibleRegion().GetSize();
typename ComplexImageType::SizeType expectedSize = inputSize;
// Inverse FFT to obtain filtered images
typename InverseFFTFilterType::Pointer inverseFFT = InverseFFTFilterType::New();
for (unsigned int level = 0; level < numberOfLevels + 1; ++level)
{
double scaleFactorPerLevel = std::pow(static_cast< double >(forwardWavelet->GetScaleFactor()),static_cast< double >(level));
for (unsigned int i = 0; i < Dimension; ++i)
{
expectedSize[i] = inputSize[i] / scaleFactorPerLevel;
expectedOrigin[i] = inputOrigin[i];
expectedSpacing[i] = inputSpacing[i] * scaleFactorPerLevel;
}
for (unsigned int band = 0; band < highSubBands; ++band)
{
unsigned int nOutput = level * forwardWavelet->GetHighPassSubBands() + band;
// Do not compute bands in low-pass level.
if (level == numberOfLevels && band == 0)
{
nOutput = forwardWavelet->GetTotalOutputs() - 1;
}
else if (level == numberOfLevels && band != 0)
{
break;
}
inverseFFT->SetInput(forwardWavelet->GetOutput(nOutput));
inverseFFT->Update();
auto itkOutputImage = inverseFFT->GetOutput();
itkOutputImage->SetSpacing(expectedSpacing);
mitk::Image::Pointer outputImage = mitk::Image::New();
CastToMitkImage(itkOutputImage, outputImage);
resultImages.push_back(outputImage);
}
}
}
template<typename TPixel, unsigned int VImageDimension>
static void ExecuteWaveletTransformation(itk::Image<TPixel, VImageDimension>* image, unsigned int numberOfLevels, unsigned int numberOfBands, mitk::BorderCondition condition, mitk::WaveletType waveletType, std::vector<mitk::Image::Pointer> &resultImages)
{
typedef itk::Point< double, VImageDimension > PointType;
typedef itk::HeldIsotropicWavelet< double, VImageDimension, PointType > HeldIsotropicWaveletType;
typedef itk::VowIsotropicWavelet< double, VImageDimension, PointType > VowIsotropicWaveletType;
typedef itk::SimoncelliIsotropicWavelet< double, VImageDimension, PointType > SimoncelliIsotropicWaveletType;
typedef itk::ShannonIsotropicWavelet< double, VImageDimension, PointType > ShannonIsotropicWaveletType;
switch (waveletType)
{
case mitk::WaveletType::Held:
ExecuteSpecificWaveletTransformation<TPixel, double, VImageDimension, HeldIsotropicWaveletType >(image, numberOfLevels, numberOfBands, condition, resultImages);
break;
case mitk::WaveletType::Shannon:
ExecuteSpecificWaveletTransformation<TPixel, double, VImageDimension, ShannonIsotropicWaveletType >(image, numberOfLevels, numberOfBands, condition, resultImages);
break;
case mitk::WaveletType::Simoncelli:
ExecuteSpecificWaveletTransformation<TPixel, double, VImageDimension, SimoncelliIsotropicWaveletType >(image, numberOfLevels, numberOfBands, condition, resultImages);
break;
case mitk::WaveletType::Vow:
ExecuteSpecificWaveletTransformation<TPixel, double, VImageDimension, VowIsotropicWaveletType >(image, numberOfLevels, numberOfBands, condition, resultImages);
break;
default:
ExecuteSpecificWaveletTransformation<TPixel, double, VImageDimension, ShannonIsotropicWaveletType >(image, numberOfLevels, numberOfBands, condition, resultImages);
break;
}
}
std::vector<mitk::Image::Pointer> mitk::TransformationOperation::WaveletForward(Image::Pointer & image, unsigned int numberOfLevels, unsigned int numberOfBands, mitk::BorderCondition condition, mitk::WaveletType waveletType)
{
std::vector<Image::Pointer> resultImages;
AccessByItk_n(image, ExecuteWaveletTransformation, (numberOfLevels, numberOfBands, condition, waveletType, resultImages));
return resultImages;
}
template<typename TPixel, unsigned int VImageDimension>
static void ExecuteImageTypeToDouble(itk::Image<TPixel, VImageDimension>* image, mitk::Image::Pointer &outputImage)
{
typedef itk::Image< TPixel, VImageDimension > ImageType;
typedef itk::Image< double, VImageDimension > DoubleImageType;
typedef itk::CastImageFilter< ImageType, DoubleImageType > CastFilterType;
typedef itk::ImageDuplicator< DoubleImageType > DuplicatorType;
// Perform FFT on input image
typename CastFilterType::Pointer castFilter = CastFilterType::New();
castFilter->SetInput(image);
castFilter->Update();
typename DuplicatorType::Pointer duplicator = DuplicatorType::New();
duplicator->SetInputImage(castFilter->GetOutput());
duplicator->Update();
CastToMitkImage(duplicator->GetOutput(), outputImage);
}
template<typename TPixel, unsigned int VImageDimension>
static void ExecuteRoundImage(itk::Image<TPixel, VImageDimension>* /*image*/, mitk::Image::Pointer resampledImage, mitk::Image::Pointer &outputImage)
{
typedef itk::Image< TPixel, VImageDimension > ImageType;
typedef itk::Image< double, VImageDimension > DoubleImageType;
typedef itk::UnaryFunctorImageFilter< DoubleImageType, ImageType, mitk::Functor::RoundValue<double, TPixel> > DefaultFilterType;
typename DoubleImageType::Pointer itkImage = DoubleImageType::New();
mitk::CastToItkImage(resampledImage, itkImage);
typename DefaultFilterType::Pointer filter = DefaultFilterType::New();
filter->SetInput(itkImage);
filter->Update();
CastToMitkImage(filter->GetOutput(), outputImage);
}
mitk::Image::Pointer mitk::TransformationOperation::ResampleImage(Image::Pointer &image, mitk::Vector3D spacingVector, mitk::ImageMappingInterpolator::Type interpolator, GridInterpolationPositionType position, bool returnAsDouble, bool roundOutput)
{
// Convert image to double if required
mitk::Image::Pointer tmpImage = image;
if (returnAsDouble)
{
AccessByItk_n(image, ExecuteImageTypeToDouble, (tmpImage));
}
auto newGeometry = image->GetGeometry()->Clone();
mitk::Vector3D spacing;
mitk::BaseGeometry::BoundsArrayType bounds = newGeometry->GetBounds();
for (int i = 0; i < 3; ++i)
{
spacing[i] = newGeometry->GetSpacing()[i];
//bounds[i*2+1] = newGeometry->GetBounds()[i * 2 + 1];
if (spacingVector[i] > 0)
{
spacing[i] = spacingVector[i];
if (position == mitk::GridInterpolationPositionType::SameSize)
{
unsigned int samples = image->GetDimensions()[i];
double currentSpacing = newGeometry->GetSpacing()[i];
double newFactor = std::floor(samples*currentSpacing / spacingVector[i]);
spacing[i] = samples * currentSpacing / newFactor;
}
}
bounds[i * 2] = 0;
bounds[i*2+1] = std::ceil(bounds[i*2+1] * newGeometry->GetSpacing()[i] *1.0 / spacing[i]);
}
mitk::Point3D origin = newGeometry->GetOrigin();
if (position == mitk::GridInterpolationPositionType::CenterAligned)
{
for (int i = 0; i < 3; ++i)
{
double oldLength = newGeometry->GetSpacing()[i] * newGeometry->GetBounds()[i*2+1];
double newLength = spacing[i] * bounds[i*2+1];
origin[i] = origin[i] - (newLength - oldLength) / 2;
}
}
newGeometry->SetSpacing(spacing);
newGeometry->SetOrigin(origin);
newGeometry->SetBounds(bounds);
mitk::Image::Pointer tmpResult = ImageMappingHelper::map(
tmpImage,
mitk::GenerateIdentityRegistration3D().GetPointer(),
false,
0.0, //Padding Value
newGeometry.GetPointer(),
false,
0, //Error Value
interpolator
);
mitk::Image::Pointer result = mitk::Image::New();
if (roundOutput)
{
AccessByItk_n(tmpImage, ExecuteRoundImage, (tmpResult, result));
}
else
{
result = tmpResult;
}
return result;
}
template<typename TPixel, unsigned int VImageDimension>
static void ExecuteImageThresholding(itk::Image<TPixel, VImageDimension>* image, mitk::Image::Pointer &resultImage)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::Image<TPixel, VImageDimension> MaskType;
typedef itk::UnaryFunctorImageFilter< ImageType, MaskType, mitk::Functor::ThresholdValue<TPixel> > DefaultFilterType;
typename DefaultFilterType::Pointer filter = DefaultFilterType::New();
filter->SetInput(image);
filter->GetFunctor().value = 0.5;
filter->Update();
CastToMitkImage(filter->GetOutput(), resultImage);
}
mitk::Image::Pointer mitk::TransformationOperation::ResampleMask(Image::Pointer &image, mitk::Vector3D spacingVector, mitk::ImageMappingInterpolator::Type interpolator, GridInterpolationPositionType position)
{
mitk::Image::Pointer result;
if (interpolator == mitk::ImageMappingInterpolator::NearestNeighbor)
{
result = TransformationOperation::ResampleImage(image, spacingVector, interpolator, position, false, false);
}
else
{
auto tmpResult = TransformationOperation::ResampleImage(image, spacingVector, interpolator, position, true, false);
AccessByItk_n(tmpResult, ExecuteImageThresholding, (result));
}
return result;
}
namespace itk
{
namespace utils
{
IndexPairType IndexToLevelBandSteerablePyramid(unsigned int linearIndex,
unsigned int levels, unsigned int bands)
{
unsigned int totalOutputs = 1 + levels * bands;
if (linearIndex > totalOutputs - 1)
{
itkGenericExceptionMacro(<< "Failed converting linearIndex " << linearIndex
<< " with levels: " << levels << " bands: " << bands <<
" to Level,Band pair : out of bounds");
}
// Low pass (band = 0).
if (linearIndex == totalOutputs - 1)
{
return std::make_pair(levels - 1, 0);
}
unsigned int band = (linearIndex) % bands + 1;
// note integer division ahead.
unsigned int level = (linearIndex) / bands;
itkAssertInDebugAndIgnoreInReleaseMacro(level < levels);
return std::make_pair(level, band);
}
// Instantiation
template
unsigned int ComputeMaxNumberOfLevels<3>(const Size< 3 >& inputSize, const unsigned int & scaleFactor);
template
unsigned int ComputeMaxNumberOfLevels<2>(const Size< 2 >& inputSize, const unsigned int & scaleFactor);
} // end namespace utils
} // end namespace itk
diff --git a/Modules/BoundingShape/include/mitkBoundingShapeInteractor.h b/Modules/BoundingShape/include/mitkBoundingShapeInteractor.h
index bedca93e42..91cc4c2c38 100644
--- a/Modules/BoundingShape/include/mitkBoundingShapeInteractor.h
+++ b/Modules/BoundingShape/include/mitkBoundingShapeInteractor.h
@@ -1,136 +1,136 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkBoundingShapeInteractor_h
#define mitkBoundingShapeInteractor_h
#include <mitkDataInteractor.h>
#include <mitkEventConfig.h>
#include <mitkGeometry3D.h>
#include <usServiceRegistration.h>
#include <MitkBoundingShapeExports.h>
namespace mitk
{
// create events for interactions
#pragma GCC visibility push(default)
- itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent);
+ itkEventMacroDeclaration(BoundingShapeInteractionEvent, itk::AnyEvent);
#pragma GCC visibility pop
/**
* @brief Basic interaction methods for mitk::GeometryData
*
* Inherit from DataInteratcor, this provides functionality of a state machine and configurable inputs.
*
* \ingroup Interaction
*/
class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor : public DataInteractor
{
public:
mitkClassMacro(BoundingShapeInteractor, DataInteractor);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
void SetDataNode(DataNode *dataNode) override;
void SetRotationEnabled(bool rotationEnabled);
protected:
BoundingShapeInteractor();
~BoundingShapeInteractor() override;
/**
* Here actions strings from the loaded state machine pattern are mapped to functions of
* the DataInteractor. These functions are called when an action from the state machine pattern is executed.
*/
void ConnectActionsAndFunctions() override;
/**
* @brief Called when a DataNode has been set/changed.
*/
void DataNodeChanged() override;
void HandlePositionChanged(const InteractionEvent *interactionEvent, Point3D &center);
/**
* @brief Checks if the mouse pointer is over the object.
*/
virtual bool CheckOverObject(const InteractionEvent *);
/**
* @brief Checks if the mouse pointer is over one of the assigned handles.
*/
virtual bool CheckOverHandles(const InteractionEvent *interactionEvent);
/**
* @brief Called if the mouse pointer is over the object indicated by a color change
*/
virtual void SelectObject(StateMachineAction *, InteractionEvent *);
/**
* @brief Called if the mouse pointer leaves the area of the object
*/
virtual void DeselectObject(StateMachineAction *, InteractionEvent *);
/**
* @brief Called if the mouse pointer is over one of the handles indicated by a color change
*/
virtual void SelectHandle(StateMachineAction *, InteractionEvent *);
/**
* @brief Performs a translation of the object relative to the mouse movement
*/
virtual void TranslateObject(StateMachineAction *, InteractionEvent *);
/**
* @brief Performs a object shape change by influencing the scaling of the initial bounding box
*/
virtual void ScaleObject(StateMachineAction *, InteractionEvent *);
/**
* @brief Initializes the movement, stores starting position
*/
virtual void InitInteraction(StateMachineAction *, InteractionEvent *interactionEvent);
/**
* @brief Deselects all Handles at the end of interaction
*/
virtual void DeselectHandles(StateMachineAction *, InteractionEvent *interactionEvent);
/**
* @brief Restore default properties of bounding box and handles
*/
virtual void RestoreNodeProperties();
/**
* @brief Initializes member variables.
*/
bool InitMembers(InteractionEvent *interactionEvent);
private:
/**
* @brief Enables default crosshair properties
*/
void EnableCrosshairNavigation();
/**
* @brief Sets limited crosshair properties (disable crosshair movement)
*/
void DisableCrosshairNavigation();
class Impl;
Impl *m_Impl;
};
}
#endif
diff --git a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp
index ce24457391..4125f76aa8 100644
--- a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp
+++ b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp
@@ -1,626 +1,628 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "../DataManagement/mitkBoundingShapeUtil.h"
#include <mitkBoundingShapeInteractor.h>
#include <mitkDisplayInteractor.h>
#include <mitkDisplayActionEventBroadcast.h>
#include <mitkInteractionConst.h>
#include <mitkInteractionEventObserver.h>
#include <mitkInteractionKeyEvent.h>
#include <mitkInteractionPositionEvent.h>
#include <mitkMouseWheelEvent.h>
#include <vtkCamera.h>
#include <vtkInteractorObserver.h>
#include <vtkInteractorStyle.h>
#include <vtkPointData.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkSmartPointer.h>
#include "usGetModuleContext.h"
#include "usModuleRegistry.h"
// Properties to allow the user to interact with the base data
const char *selectedColorPropertyName = "Bounding Shape.Selected Color";
const char *deselectedColorPropertyName = "Bounding Shape.Deselected Color";
const char *activeHandleIdPropertyName = "Bounding Shape.Active Handle ID";
const char *boundingShapePropertyName = "Bounding Shape";
namespace mitk
{
+ itkEventMacroDefinition(BoundingShapeInteractionEvent, itk::AnyEvent);
+
class BoundingShapeInteractor::Impl
{
public:
Impl() : ScrollEnabled(false), RotationEnabled(false)
{
Point3D initialPoint;
initialPoint.Fill(0.0);
for (int i = 0; i < 6; ++i)
Handles.push_back(Handle(initialPoint, i, GetHandleIndices(i)));
}
~Impl() {}
bool ScrollEnabled;
Point3D InitialPickedWorldPoint;
Point3D LastPickedWorldPoint;
Point2D InitialPickedDisplayPoint;
std::vector<Handle> Handles;
Handle ActiveHandle;
Geometry3D::Pointer OriginalGeometry;
bool RotationEnabled;
std::map<us::ServiceReferenceU, mitk::EventConfig> DisplayInteractorConfigs;
};
}
mitk::BoundingShapeInteractor::BoundingShapeInteractor() : m_Impl(new Impl)
{
}
mitk::BoundingShapeInteractor::~BoundingShapeInteractor()
{
this->RestoreNodeProperties();
delete m_Impl;
}
void mitk::BoundingShapeInteractor::ConnectActionsAndFunctions()
{
// **Conditions** that can be used in the state machine, to ensure that certain conditions are met, before actually
// executing an action
CONNECT_CONDITION("isHoveringOverObject", CheckOverObject);
CONNECT_CONDITION("isHoveringOverHandles", CheckOverHandles);
// **Function** in the statemachine patterns also referred to as **Actions**
CONNECT_FUNCTION("selectObject", SelectObject);
CONNECT_FUNCTION("deselectObject", DeselectObject);
CONNECT_FUNCTION("deselectHandles", DeselectHandles);
CONNECT_FUNCTION("initInteraction", InitInteraction);
CONNECT_FUNCTION("translateObject", TranslateObject);
CONNECT_FUNCTION("selectHandle", SelectHandle);
CONNECT_FUNCTION("scaleObject", ScaleObject);
// CONNECT_FUNCTION("rotateObject",RotateObject);
}
// RotateObject(StateMachineAction*, InteractionEvent* interactionEvent)
// void mitk::BoundingShapeInteractor::RotateGeometry(mitk::ScalarType angle, int rotationaxis, mitk::BaseGeometry*
// geometry)
//{
// mitk::Vector3D rotationAxis = geometry->GetAxisVector(rotationaxis);
// float pointX = 0.0f;
// float pointY = 0.0f;
// float pointZ = 0.0f;
// mitk::Point3D pointOfRotation;
// pointOfRotation.Fill(0.0);
// this->GetDataNode()->GetFloatProperty(anchorPointX, pointX);
// this->GetDataNode()->GetFloatProperty(anchorPointY, pointY);
// this->GetDataNode()->GetFloatProperty(anchorPointZ, pointZ);
// pointOfRotation[0] = pointX;
// pointOfRotation[1] = pointY;
// pointOfRotation[2] = pointZ;
//
// mitk::RotationOperation* doOp = new mitk::RotationOperation(OpROTATE, pointOfRotation, rotationAxis, angle);
//
// geometry->ExecuteOperation(doOp);
// delete doOp;
//}
void mitk::BoundingShapeInteractor::SetRotationEnabled(bool rotationEnabled)
{
m_Impl->RotationEnabled = rotationEnabled;
}
void mitk::BoundingShapeInteractor::DataNodeChanged()
{
mitk::DataNode::Pointer newInputNode = this->GetDataNode();
if (newInputNode == nullptr)
return;
// add color properties
mitk::ColorProperty::Pointer selectedColor =
dynamic_cast<mitk::ColorProperty *>(newInputNode->GetProperty(selectedColorPropertyName));
mitk::ColorProperty::Pointer deselectedColor =
dynamic_cast<mitk::ColorProperty *>(newInputNode->GetProperty(deselectedColorPropertyName));
if (selectedColor.IsNull())
newInputNode->AddProperty(selectedColorPropertyName, mitk::ColorProperty::New(0.0, 1.0, 0.0));
if (deselectedColor.IsNull())
newInputNode->AddProperty(deselectedColorPropertyName, mitk::ColorProperty::New(1.0, 1.0, 1.0));
newInputNode->SetProperty(boundingShapePropertyName, mitk::BoolProperty::New(true));
newInputNode->AddProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1));
newInputNode->SetProperty("layer", mitk::IntProperty::New(101));
newInputNode->SetBoolProperty("fixedLayer", mitk::BoolProperty::New(true));
newInputNode->SetBoolProperty("pickable", true);
mitk::ColorProperty::Pointer initialColor =
dynamic_cast<mitk::ColorProperty *>(newInputNode->GetProperty(deselectedColorPropertyName));
if (initialColor.IsNotNull())
{
newInputNode->SetColor(initialColor->GetColor());
}
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::BoundingShapeInteractor::HandlePositionChanged(const InteractionEvent *interactionEvent, Point3D &center)
{
GeometryData::Pointer geometryData = dynamic_cast<GeometryData *>(this->GetDataNode()->GetData());
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep);
std::vector<Point3D> cornerPoints = GetCornerPoints(geometry, true);
if (m_Impl->Handles.size() == 6)
{
// set handle positions
Point3D pointLeft = CalcAvgPoint(cornerPoints[5], cornerPoints[6]);
Point3D pointRight = CalcAvgPoint(cornerPoints[1], cornerPoints[2]);
Point3D pointTop = CalcAvgPoint(cornerPoints[0], cornerPoints[6]);
Point3D pointBottom = CalcAvgPoint(cornerPoints[7], cornerPoints[1]);
Point3D pointFront = CalcAvgPoint(cornerPoints[2], cornerPoints[7]);
Point3D pointBack = CalcAvgPoint(cornerPoints[4], cornerPoints[1]);
m_Impl->Handles[0].SetPosition(pointLeft);
m_Impl->Handles[1].SetPosition(pointRight);
m_Impl->Handles[2].SetPosition(pointTop);
m_Impl->Handles[3].SetPosition(pointBottom);
m_Impl->Handles[4].SetPosition(pointFront);
m_Impl->Handles[5].SetPosition(pointBack);
// calculate center based on half way of the distance between two opposing cornerpoints
center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]);
}
}
void mitk::BoundingShapeInteractor::SetDataNode(DataNode *node)
{
this->RestoreNodeProperties(); // if there is another node set, restore it's color
if (node == nullptr)
return;
DataInteractor::SetDataNode(node); // calls DataNodeChanged internally
this->DataNodeChanged();
}
bool mitk::BoundingShapeInteractor::CheckOverObject(const InteractionEvent *interactionEvent)
{
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return false;
GeometryData::Pointer geometryData = dynamic_cast<GeometryData *>(this->GetDataNode()->GetData());
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep);
// calculates translation based on offset+extent not on the transformation matrix (because the cube is located in the
// center not in the origin)
vtkSmartPointer<vtkMatrix4x4> imageTransform = geometry->GetVtkTransform()->GetMatrix();
Point3D center = geometry->GetCenter();
auto translation = vtkSmartPointer<vtkTransform>::New();
auto transform = vtkSmartPointer<vtkTransform>::New();
translation->Translate(center[0] - imageTransform->GetElement(0, 3),
center[1] - imageTransform->GetElement(1, 3),
center[2] - imageTransform->GetElement(2, 3));
transform->SetMatrix(imageTransform);
transform->PostMultiply();
transform->Concatenate(translation);
transform->Update();
mitk::Vector3D extent;
for (unsigned int i = 0; i < 3; ++i)
extent[i] = (geometry->GetExtent(i));
Point3D currentWorldPosition;
Point2D currentDisplayPosition = positionEvent->GetPointerPositionOnScreen();
interactionEvent->GetSender()->DisplayToWorld(currentDisplayPosition, currentWorldPosition);
ScalarType transformedPosition[4];
transformedPosition[0] = currentWorldPosition[0];
transformedPosition[1] = currentWorldPosition[1];
transformedPosition[2] = currentWorldPosition[2];
transformedPosition[3] = 1;
// transform point from world to object coordinates
transform->GetInverse()->TransformPoint(transformedPosition, transformedPosition);
// check if the world point is within bounds
bool isInside = (transformedPosition[0] >= (-extent[0] / 2.0)) && (transformedPosition[0] <= (extent[0] / 2.0)) &&
(transformedPosition[1] >= (-extent[1] / 2.0)) && (transformedPosition[1] <= (extent[1] / 2.0)) &&
(transformedPosition[2] >= (-extent[2] / 2.0)) && (transformedPosition[2] <= (extent[2] / 2.0));
return isInside;
}
bool mitk::BoundingShapeInteractor::CheckOverHandles(const InteractionEvent *interactionEvent)
{
Point3D boundingBoxCenter;
HandlePositionChanged(interactionEvent, boundingBoxCenter);
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return false;
Point2D displayCenterPoint;
// to do: change to actual time step (currently not necessary because geometry remains the same for each timestep
int timeStep = 0;
GeometryData::Pointer geometryData = dynamic_cast<GeometryData *>(this->GetDataNode()->GetData());
BaseGeometry::Pointer geometry = geometryData->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep);
std::vector<Point3D> cornerPoints = GetCornerPoints(geometry, true);
interactionEvent->GetSender()->WorldToDisplay(boundingBoxCenter, displayCenterPoint);
double scale = interactionEvent->GetSender()->GetScaleFactorMMPerDisplayUnit(); // GetDisplaySizeInMM
mitk::DoubleProperty::Pointer handleSizeProperty =
dynamic_cast<mitk::DoubleProperty *>(this->GetDataNode()->GetProperty("Bounding Shape.Handle Size Factor"));
ScalarType initialHandleSize;
if (handleSizeProperty != nullptr)
initialHandleSize = handleSizeProperty->GetValue();
else
initialHandleSize = 1.0 / 40.0;
mitk::Point2D displaysize = interactionEvent->GetSender()->GetDisplaySizeInMM();
ScalarType handlesize = ((displaysize[0] + displaysize[1]) / 2.0) * initialHandleSize;
unsigned int handleNum = 0;
for (auto &handle : m_Impl->Handles)
{
Point2D centerpoint;
interactionEvent->GetSender()->WorldToDisplay(handle.GetPosition(), centerpoint);
Point2D currentDisplayPosition = positionEvent->GetPointerPositionOnScreen();
if ((currentDisplayPosition.EuclideanDistanceTo(centerpoint) < (handlesize / scale)) &&
(currentDisplayPosition.EuclideanDistanceTo(displayCenterPoint) >
(handlesize / scale))) // check if mouse is hovering over center point
{
handle.SetActive(true);
m_Impl->ActiveHandle = handle;
this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName,
mitk::IntProperty::New(handleNum++));
this->GetDataNode()->GetData()->Modified();
RenderingManager::GetInstance()->RequestUpdateAll();
return true;
}
else
{
handleNum++;
handle.SetActive(false);
}
this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1));
}
return false;
}
void mitk::BoundingShapeInteractor::SelectHandle(StateMachineAction *, InteractionEvent *)
{
this->DisableCrosshairNavigation();
DataNode::Pointer node = this->GetDataNode();
if (node.IsNull())
return;
mitk::ColorProperty::Pointer selectedColor =
dynamic_cast<mitk::ColorProperty *>(node->GetProperty(deselectedColorPropertyName));
if (selectedColor.IsNotNull())
{
this->GetDataNode()->GetPropertyList()->SetProperty("color", selectedColor);
}
this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date
this->GetDataNode()->GetData()->Modified();
RenderingManager::GetInstance()->RequestUpdateAll();
return;
}
void mitk::BoundingShapeInteractor::DeselectHandles(StateMachineAction *, InteractionEvent *)
{
this->DisableCrosshairNavigation();
DataNode::Pointer node = this->GetDataNode();
if (node.IsNull())
return;
this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1));
this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date
this->GetDataNode()->GetData()->Modified();
RenderingManager::GetInstance()->RequestUpdateAll();
return;
}
void mitk::BoundingShapeInteractor::SelectObject(StateMachineAction *, InteractionEvent *)
{
this->DisableCrosshairNavigation(); // disable crosshair interaction and scolling if user is hovering over the object
DataNode::Pointer node = this->GetDataNode();
if (node.IsNull())
return;
mitk::ColorProperty::Pointer selectedColor =
dynamic_cast<mitk::ColorProperty *>(node->GetProperty(selectedColorPropertyName));
if (selectedColor.IsNotNull())
{
node->GetPropertyList()->SetProperty("color", selectedColor);
}
this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date
this->GetDataNode()->GetData()->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
return;
}
void mitk::BoundingShapeInteractor::DeselectObject(StateMachineAction *, InteractionEvent *)
{
this->EnableCrosshairNavigation(); // enable crosshair interaction and scolling if user is hovering over the object
DataNode::Pointer node = this->GetDataNode();
if (node.IsNull())
return;
mitk::ColorProperty::Pointer deselectedColor =
dynamic_cast<mitk::ColorProperty *>(node->GetProperty(deselectedColorPropertyName));
if (deselectedColor.IsNotNull())
{
node->GetPropertyList()->SetProperty("color", deselectedColor);
}
this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date
this->GetDataNode()->GetData()->Modified();
RenderingManager::GetInstance()->RequestUpdateAll();
return;
}
void mitk::BoundingShapeInteractor::InitInteraction(StateMachineAction *, InteractionEvent *interactionEvent)
{
InitMembers(interactionEvent);
}
bool mitk::BoundingShapeInteractor::InitMembers(InteractionEvent *interactionEvent)
{
auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return false;
// get initial position coordinates
m_Impl->InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen();
m_Impl->InitialPickedWorldPoint = positionEvent->GetPositionInWorld();
m_Impl->LastPickedWorldPoint = positionEvent->GetPositionInWorld();
return true;
}
void mitk::BoundingShapeInteractor::TranslateObject(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
mitk::BaseGeometry::Pointer geometry =
this->GetDataNode()->GetData()->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep);
Vector3D spacing = geometry->GetSpacing();
Point3D currentPickedPoint;
interactionEvent->GetSender()->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), currentPickedPoint);
Vector3D interactionMove;
// pixel aligned shifting of the bounding box
interactionMove[0] = std::round((currentPickedPoint[0] - m_Impl->LastPickedWorldPoint[0]) / spacing[0]) * spacing[0];
interactionMove[1] = std::round((currentPickedPoint[1] - m_Impl->LastPickedWorldPoint[1]) / spacing[1]) * spacing[1];
interactionMove[2] = std::round((currentPickedPoint[2] - m_Impl->LastPickedWorldPoint[2]) / spacing[2]) * spacing[2];
if ((interactionMove[0] + interactionMove[1] + interactionMove[2]) !=
0.0) // only update current position if a movement occured
{
m_Impl->LastPickedWorldPoint = currentPickedPoint;
geometry->SetOrigin(geometry->GetOrigin() + interactionMove);
this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date
this->GetDataNode()->GetData()->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
return;
}
void mitk::BoundingShapeInteractor::ScaleObject(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
GeometryData::Pointer geometryData = dynamic_cast<GeometryData *>(this->GetDataNode()->GetData());
Point3D handlePickedPoint = m_Impl->ActiveHandle.GetPosition();
Point3D currentPickedPoint;
interactionEvent->GetSender()->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), currentPickedPoint);
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep);
Vector3D spacing = geometry->GetSpacing();
// pixel aligned bounding box
Vector3D interactionMove;
interactionMove[0] = (currentPickedPoint[0] - m_Impl->LastPickedWorldPoint[0]);
interactionMove[1] = (currentPickedPoint[1] - m_Impl->LastPickedWorldPoint[1]);
interactionMove[2] = (currentPickedPoint[2] - m_Impl->LastPickedWorldPoint[2]);
std::vector<int> faces = m_Impl->ActiveHandle.GetFaceIndices();
auto pointscontainer = mitk::BoundingBox::PointsContainer::New();
// calculate cornerpoints from geometry plus visualization offset
std::vector<Point3D> cornerPoints = GetCornerPoints(geometry, true);
unsigned int num = 0;
for (const auto &point : cornerPoints)
{
pointscontainer->InsertElement(num++, point);
}
// calculate center based on half way of the distance between two opposing cornerpoints
mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]);
Vector3D faceNormal;
faceNormal[0] = handlePickedPoint[0] - center[0];
faceNormal[1] = handlePickedPoint[1] - center[1];
faceNormal[2] = handlePickedPoint[2] - center[2];
Vector3D faceShift = ((faceNormal * interactionMove) / (faceNormal.GetNorm() * faceNormal.GetNorm())) * faceNormal;
// calculate cornerpoints from geometry without visualization offset to update actual geometry
cornerPoints = GetCornerPoints(geometry, false);
num = 0;
for (const auto &point : cornerPoints)
{
pointscontainer->InsertElement(num++, point);
}
bool positionChangeThreshold = true;
for (int numFaces = 0; numFaces < 8; numFaces++) // estimate the corresponding face and shift its assigned points
{
if ((numFaces != faces[0]) && (numFaces != faces[1]) && (numFaces != faces[2]) && (numFaces != faces[3]))
{
Point3D point = pointscontainer->GetElement(numFaces);
if (m_Impl->RotationEnabled) // apply if geometry is rotated at a pixel aligned shift is not possible
{
point[0] += faceShift[0];
point[1] += faceShift[1];
point[2] += faceShift[2];
}
else // shift pixelwise
{
point[0] += std::round(faceShift[0] / spacing[0]) * spacing[0];
point[1] += std::round(faceShift[1] / spacing[1]) * spacing[1];
point[2] += std::round(faceShift[2] / spacing[2]) * spacing[2];
}
if (point == pointscontainer->GetElement(numFaces))
positionChangeThreshold = false;
else
m_Impl->LastPickedWorldPoint = point;
pointscontainer->InsertElement(numFaces, point);
}
}
if (positionChangeThreshold) // update only if bounding box is shifted at least by one pixel
{
auto inverse = mitk::AffineTransform3D::New();
geometry->GetIndexToWorldTransform()->GetInverse(inverse);
for (unsigned int pointid = 0; pointid < 8; pointid++)
{
pointscontainer->InsertElement(pointid, inverse->TransformPoint(pointscontainer->GetElement(pointid)));
}
auto bbox = mitk::BoundingBox::New();
bbox->SetPoints(pointscontainer);
bbox->ComputeBoundingBox();
mitk::Point3D BBmin = bbox->GetMinimum();
mitk::Point3D BBmax = bbox->GetMaximum();
if (std::abs(BBmin[0] - BBmax[0]) > 0.01 && std::abs(BBmin[1] - BBmax[1]) > 0.01 &&
std::abs(BBmin[2] - BBmax[2]) > 0.01) // TODO: check if the extent is greater than zero
{
geometry->SetBounds(bbox->GetBounds());
geometry->Modified();
this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date
this->GetDataNode()->GetData()->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
return;
}
void mitk::BoundingShapeInteractor::RestoreNodeProperties()
{
mitk::DataNode::Pointer inputNode = this->GetDataNode();
if (inputNode.IsNull())
return;
mitk::ColorProperty::Pointer color = (mitk::ColorProperty::New(1.0, 1.0, 1.0));
if (color.IsNotNull())
{
inputNode->GetPropertyList()->SetProperty("color", color);
}
inputNode->SetProperty("layer", mitk::IntProperty::New(99));
inputNode->SetProperty(boundingShapePropertyName, mitk::BoolProperty::New(false));
inputNode->GetPropertyList()->DeleteProperty(activeHandleIdPropertyName);
EnableCrosshairNavigation();
// update rendering
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::BoundingShapeInteractor::EnableCrosshairNavigation()
{
// enable the crosshair navigation
// Re-enabling InteractionEventObservers that have been previously disabled for legacy handling of Tools
// in new interaction framework
for (auto it = m_Impl->DisplayInteractorConfigs.begin();
it != m_Impl->DisplayInteractorConfigs.end();
++it)
{
if (it->first)
{
mitk::DisplayInteractor *displayInteractor = static_cast<mitk::DisplayInteractor *>(
us::GetModuleContext()->GetService<mitk::InteractionEventObserver>(it->first));
if (displayInteractor != nullptr)
{
// here the regular configuration is loaded again
displayInteractor->SetEventConfig(it->second);
}
mitk::DisplayActionEventBroadcast *displayActionEventBroadcast = static_cast<mitk::DisplayActionEventBroadcast *>(
us::GetModuleContext()->GetService<mitk::InteractionEventObserver>(it->first));
if (displayActionEventBroadcast != nullptr)
{
// here the regular configuration is loaded again
displayActionEventBroadcast->SetEventConfig(it->second);
}
}
}
m_Impl->DisplayInteractorConfigs.clear();
m_Impl->ScrollEnabled = true;
}
void mitk::BoundingShapeInteractor::DisableCrosshairNavigation()
{
// dont deactivate twice, else we will clutter the config list ...
if (m_Impl->ScrollEnabled == false)
return;
// As a legacy solution the display interaction of the new interaction framework is disabled here to avoid conflicts
// with tools
// Note: this only affects InteractionEventObservers (formerly known as Listeners) all DataNode specific interaction
// will still be enabled
m_Impl->DisplayInteractorConfigs.clear();
std::vector<us::ServiceReference<mitk::InteractionEventObserver>> listEventObserver =
us::GetModuleContext()->GetServiceReferences<mitk::InteractionEventObserver>();
for (auto it = listEventObserver.begin();
it != listEventObserver.end();
++it)
{
auto *displayInteractor =
dynamic_cast<mitk::DisplayInteractor *>(us::GetModuleContext()->GetService<mitk::InteractionEventObserver>(*it));
if (displayInteractor != nullptr)
{
// remember the original configuration
m_Impl->DisplayInteractorConfigs.insert(std::make_pair(*it, displayInteractor->GetEventConfig()));
// here the alternative configuration is loaded
displayInteractor->AddEventConfig("DisplayConfigBlockLMB.xml");
}
auto *displayActionEventBroadcast =
dynamic_cast<mitk::DisplayActionEventBroadcast *>(us::GetModuleContext()->GetService<mitk::InteractionEventObserver>(*it));
if (displayActionEventBroadcast != nullptr)
{
// remember the original configuration
m_Impl->DisplayInteractorConfigs.insert(std::make_pair(*it, displayActionEventBroadcast->GetEventConfig()));
// here the alternative configuration is loaded
displayActionEventBroadcast->AddEventConfig("DisplayConfigBlockLMB.xml");
}
}
m_Impl->ScrollEnabled = false;
}
diff --git a/Modules/CMakeLists.txt b/Modules/CMakeLists.txt
index b6f9fe8226..edec620255 100644
--- a/Modules/CMakeLists.txt
+++ b/Modules/CMakeLists.txt
@@ -1,28 +1,28 @@
#-----------------------------------------------------------------------------
# Configure the CppMicroServices build
#-----------------------------------------------------------------------------
set(CppMicroServices_DIR_default "${CMAKE_CURRENT_BINARY_DIR}/CppMicroServices")
if(NOT CppMicroServices_DIR)
set(CppMicroServices_DIR ${CppMicroServices_DIR_default} CACHE PATH "Path to the CppMicroServices library" FORCE)
endif()
mark_as_advanced(CppMicroServices_DIR)
if ("${CppMicroServices_DIR}" STREQUAL "${CppMicroServices_DIR_default}")
set(US_ENABLE_AUTOLOADING_SUPPORT ON)
set(US_ENABLE_THREADING_SUPPORT ON)
# Don't create a "doc" target and don't install the documentation files
set(US_NO_DOCUMENTATION ON)
# Don't install anything for now (we use the CMake BundleUtilities script)
set(US_NO_INSTALL 1)
if(BUILD_TESTING)
set(US_BUILD_TESTING ON)
endif()
- mitkFunctionCheckCompilerFlags("-std=c++14" US_CXX_FLAGS)
+ mitkFunctionCheckCompilerFlags("-std=c++${MITK_CXX_STANDARD}" US_CXX_FLAGS)
add_subdirectory(CppMicroServices)
endif()
diff --git a/Modules/CameraCalibration/mitkCameraIntrinsics.cpp b/Modules/CameraCalibration/mitkCameraIntrinsics.cpp
index fbb1ce2894..2613586ad7 100644
--- a/Modules/CameraCalibration/mitkCameraIntrinsics.cpp
+++ b/Modules/CameraCalibration/mitkCameraIntrinsics.cpp
@@ -1,508 +1,506 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkCameraIntrinsics.h"
-#include <itkMutexLockHolder.h>
#include <mitkEndoMacros.h>
#include <mitkEndoDebug.h>
#include <tinyxml2.h>
mitk::CameraIntrinsics::CameraIntrinsics()
- : m_Valid(false), m_Mutex(itk::FastMutexLock::New())
+ : m_Valid(false)
{
m_CameraMatrix = cv::Mat::zeros(3, 3, cv::DataType<double>::type);
m_CameraMatrix.at<double>(2,2) = 1.0;
m_DistorsionCoeffs = cv::Mat::zeros(1, 5, cv::DataType<double>::type);
}
mitk::CameraIntrinsics::CameraIntrinsics(const CameraIntrinsics& other)
: itk::Object()
, mitk::XMLSerializable()
, m_Valid(false)
- , m_Mutex(itk::FastMutexLock::New())
{
this->Copy(&other);
}
mitk::CameraIntrinsics::~CameraIntrinsics()
{
}
bool mitk::CameraIntrinsics::Equals( const CameraIntrinsics* other ) const
{
return other->GetDistorsionCoeffsAsPoint4D()==
this->GetDistorsionCoeffsAsPoint4D() &&
other->GetFocalPoint()==
this->GetFocalPoint() &&
other->GetPrincipalPoint()
== this->GetPrincipalPoint();
}
void mitk::CameraIntrinsics::Copy(const CameraIntrinsics* other)
{
this->SetIntrinsics( other->GetCameraMatrix().clone()
, other->GetDistorsionCoeffs().clone() );
this->SetValid(other->m_Valid);
}
bool mitk::CameraIntrinsics::IsValid() const
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*m_Mutex);
+ std::lock_guard<std::mutex> lock(m_Mutex);
return m_Valid;
}
vnl_matrix_fixed<mitk::ScalarType, 3, 3>
mitk::CameraIntrinsics::GetVnlCameraMatrix() const
{
vnl_matrix_fixed<mitk::ScalarType, 3, 3> mat;
mat.set_identity();
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*m_Mutex);
+ std::lock_guard<std::mutex> lock(m_Mutex);
mat(0,0) = m_CameraMatrix.at<double>(0,0);
mat(1,1) = m_CameraMatrix.at<double>(1,1);
mat(0,2) = m_CameraMatrix.at<double>(0,2);
mat(1,2) = m_CameraMatrix.at<double>(1,2);
}
return mat;
}
void mitk::CameraIntrinsics::SetCameraMatrix(
const vnl_matrix_fixed<mitk::ScalarType, 3, 3>& _CameraMatrix )
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*m_Mutex);
+ std::lock_guard<std::mutex> lock(m_Mutex);
m_CameraMatrix.at<double>(0,0) = _CameraMatrix(0,0);
m_CameraMatrix.at<double>(1,1) = _CameraMatrix(1,1);
m_CameraMatrix.at<double>(0,2) = _CameraMatrix(0,2);
m_CameraMatrix.at<double>(1,2) = _CameraMatrix(1,2);
}
vnl_matrix_fixed<mitk::ScalarType, 3, 4>
mitk::CameraIntrinsics::GetVnlCameraMatrix3x4() const
{
vnl_matrix_fixed<mitk::ScalarType, 3, 4> mat;
mat.fill(0);
mat.update( this->GetVnlCameraMatrix().as_matrix() );
return mat;
}
void mitk::CameraIntrinsics::SetIntrinsics( const cv::Mat& _CameraMatrix
, const cv::Mat& _DistorsionCoeffs)
{
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*m_Mutex);
+ std::lock_guard<std::mutex> lock(m_Mutex);
if( _CameraMatrix.cols != 3 || _CameraMatrix.rows != 3)
throw std::invalid_argument("Wrong format of camera matrix. Should be 3x3"
" double.");
endoAssertMsg( (_DistorsionCoeffs.cols == 5) &&
_DistorsionCoeffs.rows == 1, "Wrong format of distorsion coefficients"
" vector. Should be 5x1 double.");
m_CameraMatrix = _CameraMatrix.clone();
m_DistorsionCoeffs = _DistorsionCoeffs.clone();
m_Valid = true;
}
this->Modified();
}
void mitk::CameraIntrinsics::SetIntrinsics( const mitk::Point3D& focalPoint,
const mitk::Point3D& principalPoint,
const mitk::Point4D& distortionCoefficients)
{
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*m_Mutex);
+ std::lock_guard<std::mutex> lock(m_Mutex);
m_CameraMatrix.at<double>(0,0) = focalPoint[0];
m_CameraMatrix.at<double>(1,1) = focalPoint[1];
m_CameraMatrix.at<double>(0,2) = principalPoint[0];
m_CameraMatrix.at<double>(1,2) = principalPoint[1];
m_DistorsionCoeffs.at<double>(0,0) = distortionCoefficients[0];
m_DistorsionCoeffs.at<double>(0,1) = distortionCoefficients[1];
m_DistorsionCoeffs.at<double>(0,2) = distortionCoefficients[2];
m_DistorsionCoeffs.at<double>(0,3) = distortionCoefficients[3];
}
this->Modified();
}
void mitk::CameraIntrinsics::SetFocalLength( double x, double y )
{
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*m_Mutex);
+ std::lock_guard<std::mutex> lock(m_Mutex);
m_CameraMatrix.at<double>(0,0) = x;
m_CameraMatrix.at<double>(1,1) = y;
}
this->Modified();
}
void mitk::CameraIntrinsics::SetPrincipalPoint( double x, double y )
{
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*m_Mutex);
+ std::lock_guard<std::mutex> lock(m_Mutex);
m_CameraMatrix.at<double>(0,2) = x;
m_CameraMatrix.at<double>(1,2) = y;
}
this->Modified();
}
void mitk::CameraIntrinsics::SetDistorsionCoeffs( double k1, double k2,
double p1, double p2 )
{
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*m_Mutex);
+ std::lock_guard<std::mutex> lock(m_Mutex);
m_DistorsionCoeffs.at<double>(0,0) = k1;
m_DistorsionCoeffs.at<double>(0,1) = k2;
m_DistorsionCoeffs.at<double>(0,2) = p1;
m_DistorsionCoeffs.at<double>(0,3) = p2;
}
this->Modified();
}
cv::Mat mitk::CameraIntrinsics::GetCameraMatrix() const
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*m_Mutex);
+ std::lock_guard<std::mutex> lock(m_Mutex);
return m_CameraMatrix.clone(); // return a copy of this small matrix
}
cv::Mat mitk::CameraIntrinsics::GetDistorsionCoeffs() const
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*m_Mutex);
+ std::lock_guard<std::mutex> lock(m_Mutex);
return m_DistorsionCoeffs.clone(); // return a copy of this small matrix
}
cv::Mat mitk::CameraIntrinsics::GetDistorsionCoeffs()
{
const CameraIntrinsics* intrinsics = this;
return intrinsics->GetDistorsionCoeffs();
}
std::string mitk::CameraIntrinsics::ToString() const
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*m_Mutex);
+ std::lock_guard<std::mutex> lock(m_Mutex);
std::ostringstream s; s.precision(12);
const cv::Mat& CameraMatrix = m_CameraMatrix;
const cv::Mat& DistorsionCoeffs = m_DistorsionCoeffs;
s.str(""); s << this->GetNameOfClass() << ": ";
s << "fx = " << CameraMatrix.at<double>(0,0);
s << ", fy = " << CameraMatrix.at<double>(1,1);
s << ", cx = " << CameraMatrix.at<double>(0,2);
s << ", cy = " << CameraMatrix.at<double>(1,2);
s << ", k1 = " << DistorsionCoeffs.at<double>(0,0);
s << ", k2 = " << DistorsionCoeffs.at<double>(0,1);
s << ", p1 = " << DistorsionCoeffs.at<double>(0,2);
s << ", p2 = " << DistorsionCoeffs.at<double>(0,3);
//s << ", k3 = " << DistorsionCoeffs.at<double>(0,4);
return s.str();
}
void mitk::CameraIntrinsics::ToXML(tinyxml2::XMLElement* elem) const
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*m_Mutex);
+ std::lock_guard<std::mutex> lock(m_Mutex);
elem->SetValue(this->GetNameOfClass());
std::ostringstream s; s.precision(12);
const cv::Mat& CameraMatrix = m_CameraMatrix;
s.str(""); s << CameraMatrix.at<double>(0,0);
elem->SetAttribute( "fx", s.str().c_str() );
s.str(""); s << CameraMatrix.at<double>(1,1);
elem->SetAttribute( "fy", s.str().c_str());
s.str(""); s << CameraMatrix.at<double>(0,2);
elem->SetAttribute( "cx", s.str().c_str());
s.str(""); s << CameraMatrix.at<double>(1,2);
elem->SetAttribute( "cy", s.str().c_str());
const cv::Mat& DistorsionCoeffs = m_DistorsionCoeffs;
s.str(""); s << DistorsionCoeffs.at<double>(0,0);
elem->SetAttribute( "k1", s.str().c_str());
s.str(""); s << DistorsionCoeffs.at<double>(0,1);
elem->SetAttribute( "k2", s.str().c_str());
s.str(""); s << DistorsionCoeffs.at<double>(0,2);
elem->SetAttribute( "p1", s.str().c_str());
s.str(""); s << DistorsionCoeffs.at<double>(0,3);
elem->SetAttribute( "p2", s.str().c_str());
elem->SetAttribute("Valid", static_cast<int>(m_Valid));
//s.str(""); s << DistorsionCoeffs.at<double>(4,0);
//elem->SetAttribute( "k3", s.str().c_str() );
}
void mitk::CameraIntrinsics::FromGMLCalibrationXML(const tinyxml2::XMLElement* elem)
{
assert( elem );
assert( std::string(elem->Value()) == "results" );
cv::Mat CameraMatrix = cv::Mat::zeros(3, 3, cv::DataType<double>::type);
CameraMatrix.at<double>(2,2) = 1.0;
cv::Mat DistorsionCoeffs = cv::Mat::zeros(1, 5, cv::DataType<double>::type);
const auto* focus_lenXElem = elem->FirstChildElement("focus_lenX");
endoAssert( focus_lenXElem != nullptr );
CameraMatrix.at<double>(0,0) = atof( focus_lenXElem->GetText() );
const auto* focus_lenYElem = elem->FirstChildElement("focus_lenY");
endoAssert( focus_lenYElem != nullptr );
CameraMatrix.at<double>(1,1) = atof( focus_lenYElem->GetText() );
const auto* PrincipalXElem = elem->FirstChildElement("PrincipalX");
endoAssert( PrincipalXElem != nullptr );
CameraMatrix.at<double>(0,2) = atof( PrincipalXElem->GetText() );
const auto* PrincipalYElem = elem->FirstChildElement("PrincipalY");
endoAssert( PrincipalYElem != nullptr );
CameraMatrix.at<double>(1,2) = atof( PrincipalYElem->GetText() );
// DISTORSION COEFFS
const auto* Dist1Elem = elem->FirstChildElement("Dist1");
endoAssert( Dist1Elem != nullptr );
DistorsionCoeffs.at<double>(0,0) = atof( Dist1Elem->GetText() );
const auto* Dist2Elem = elem->FirstChildElement("Dist2");
endoAssert( Dist2Elem != nullptr );
DistorsionCoeffs.at<double>(0,1) = atof( Dist2Elem->GetText() );
const auto* Dist3Elem = elem->FirstChildElement("Dist3");
endoAssert( Dist3Elem != nullptr );
DistorsionCoeffs.at<double>(0,2) = atof( Dist3Elem->GetText() );
const auto* Dist4Elem = elem->FirstChildElement("Dist4");
endoAssert( Dist4Elem != nullptr );
DistorsionCoeffs.at<double>(0,3) = atof( Dist4Elem->GetText() );
int valid = 0;
elem->QueryIntAttribute("Valid", &valid);
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*m_Mutex);
+ std::lock_guard<std::mutex> lock(m_Mutex);
m_Valid = static_cast<bool>(valid);
m_CameraMatrix = CameraMatrix;
m_DistorsionCoeffs = DistorsionCoeffs;
}
this->Modified();
}
void mitk::CameraIntrinsics::FromXML(const tinyxml2::XMLElement* elem)
{
endoAssert ( elem );
MITK_DEBUG << elem->Value();
const char* filename = elem->Attribute("file");
if(nullptr != filename)
{
this->FromXMLFile(filename);
return;
}
else if(strcmp(elem->Value(), "CalibrationProject") == 0)
{
this->FromGMLCalibrationXML(elem->FirstChildElement("results"));
return;
}
assert ( elem );
if(strcmp(elem->Value(), this->GetNameOfClass()) != 0)
elem = elem->FirstChildElement(this->GetNameOfClass());
std::ostringstream err;
// CAMERA MATRIX
cv::Mat CameraMatrix = cv::Mat::zeros(3, 3, cv::DataType<double>::type);
CameraMatrix.at<double>(2,2) = 1.0;
double val = 0.0;
if(elem->QueryDoubleAttribute("fx", &val) == tinyxml2::XML_SUCCESS)
CameraMatrix.at<double>(0,0) = val;
else
err << "fx, ";
if(elem->QueryDoubleAttribute("fy", &val) == tinyxml2::XML_SUCCESS)
CameraMatrix.at<double>(1,1) = val;
else
err << "fy, ";
if(elem->QueryDoubleAttribute("cx", &val) == tinyxml2::XML_SUCCESS)
CameraMatrix.at<double>(0,2) = val;
else
err << "cx, ";
if(elem->QueryDoubleAttribute("cy", &val) == tinyxml2::XML_SUCCESS)
CameraMatrix.at<double>(1,2) = val;
else
err << "cy, ";
// DISTORSION COEFFS
endodebug( "creating DistorsionCoeffs from XML file")
cv::Mat DistorsionCoeffs = cv::Mat::zeros(1, 5, cv::DataType<double>::type);
if(elem->QueryDoubleAttribute("k1", &val) == tinyxml2::XML_SUCCESS)
DistorsionCoeffs.at<double>(0,0) = val;
else
err << "k1, ";
if(elem->QueryDoubleAttribute("k2", &val) == tinyxml2::XML_SUCCESS)
DistorsionCoeffs.at<double>(0,1) = val;
else
err << "k2, ";
if(elem->QueryDoubleAttribute("p1", &val) == tinyxml2::XML_SUCCESS)
DistorsionCoeffs.at<double>(0,2) = val;
else
err << "p1, ";
if(elem->QueryDoubleAttribute("p2", &val) == tinyxml2::XML_SUCCESS)
DistorsionCoeffs.at<double>(0,3) = val;
else
err << "p2, ";
DistorsionCoeffs.at<double>(0,4) = 0.0;
/*if(elem->QueryDoubleAttribute("k3", &val) == tinyxml2::XML_SUCCESS)
DistorsionCoeffs.at<double>(4,0) = val;
else
err << "k3, ";*/
std::string errorStr = err.str();
int errLength = errorStr.length();
if(errLength > 0)
{
errorStr = errorStr.substr(0, errLength-2);
errorStr.append(" not found");
throw std::invalid_argument(err.str());
}
int valid = 0;
elem->QueryIntAttribute("Valid", &valid);
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*m_Mutex);
+ std::lock_guard<std::mutex> lock(m_Mutex);
m_Valid = static_cast<bool>(valid);
m_CameraMatrix = CameraMatrix;
m_DistorsionCoeffs = DistorsionCoeffs;
}
this->Modified();
}
double mitk::CameraIntrinsics::GetFocalLengthX() const
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*m_Mutex);
+ std::lock_guard<std::mutex> lock(m_Mutex);
double FocalLengthX = m_CameraMatrix.at<double>(0,0);
return FocalLengthX;
}
double mitk::CameraIntrinsics::GetFocalLengthY() const
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*m_Mutex);
+ std::lock_guard<std::mutex> lock(m_Mutex);
double FocalLengthY = m_CameraMatrix.at<double>(1,1);;
return FocalLengthY;
}
double mitk::CameraIntrinsics::GetPrincipalPointX() const
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*m_Mutex);
+ std::lock_guard<std::mutex> lock(m_Mutex);
double PrincipalPointX = m_CameraMatrix.at<double>(0,2);
return PrincipalPointX;
}
double mitk::CameraIntrinsics::GetPrincipalPointY() const
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*m_Mutex);
+ std::lock_guard<std::mutex> lock(m_Mutex);
double PrincipalPointY = m_CameraMatrix.at<double>(1,2);
return PrincipalPointY;
}
mitk::Point4D mitk::CameraIntrinsics::GetDistorsionCoeffsAsPoint4D() const
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*m_Mutex);
+ std::lock_guard<std::mutex> lock(m_Mutex);
mitk::Point4D coeffs;
coeffs[0] = m_DistorsionCoeffs.at<double>(0,0);
coeffs[1] = m_DistorsionCoeffs.at<double>(0,1);
coeffs[2] = m_DistorsionCoeffs.at<double>(0,2);
coeffs[3] = m_DistorsionCoeffs.at<double>(0,3);
return coeffs;
}
mitk::Point3D mitk::CameraIntrinsics::GetFocalPoint() const
{
mitk::Point3D p;
p[0] = this->GetFocalLengthX();
p[1] = this->GetFocalLengthY();
p[2] = 0;
return p;
}
mitk::Point3D mitk::CameraIntrinsics::GetPrincipalPoint() const
{
mitk::Point3D p;
p[0] = this->GetPrincipalPointX();
p[1] = this->GetPrincipalPointY();
p[2] = 0;
return p;
}
vnl_vector_fixed<mitk::ScalarType, 2>
mitk::CameraIntrinsics::GetFocalPointAsVnlVector() const
{
vnl_vector_fixed<mitk::ScalarType, 2> vec;
vec[0] = this->GetFocalLengthX();
vec[1] = this->GetFocalLengthY();
return vec;
}
vnl_vector_fixed<mitk::ScalarType, 2>
mitk::CameraIntrinsics::GetPrincipalPointAsVnlVector() const
{
vnl_vector_fixed<mitk::ScalarType, 2> vec;
vec[0] = this->GetPrincipalPointX();
vec[1] = this->GetPrincipalPointY();
return vec;
}
std::ostream& operator<< (std::ostream& os, mitk::CameraIntrinsics::Pointer p)
{
os << p->ToString();
return os;
}
std::string mitk::CameraIntrinsics::GetString()
{
return this->ToString();
}
std::string mitk::CameraIntrinsics::ToOctaveString(
const std::string& varName)
{
std::ostringstream s;
s << varName << " = [" << this->GetFocalLengthX() << " 0 "
<< this->GetPrincipalPointX() << "; 0 " <<
this->GetFocalLengthY() << " " << this->GetPrincipalPointY() << ";"
<< " 0 0 1 ];";
return s.str();
}
void mitk::CameraIntrinsics::SetValid( bool valid )
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*m_Mutex);
+ std::lock_guard<std::mutex> lock(m_Mutex);
m_Valid = valid;
}
itk::LightObject::Pointer mitk::CameraIntrinsics::InternalClone() const
{
itk::LightObject::Pointer result(new Self(*this));
result->UnRegister();
return result;
}
diff --git a/Modules/CameraCalibration/mitkCameraIntrinsics.h b/Modules/CameraCalibration/mitkCameraIntrinsics.h
index a36cbf0a7f..77c7a9aee7 100644
--- a/Modules/CameraCalibration/mitkCameraIntrinsics.h
+++ b/Modules/CameraCalibration/mitkCameraIntrinsics.h
@@ -1,141 +1,142 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkCameraIntrinsics_h
#define mitkCameraIntrinsics_h
#include <mitkCommon.h>
#include <mitkNumericTypes.h>
#include <itkDataObject.h>
-#include <itkFastMutexLock.h>
#include <vnl/vnl_matrix_fixed.h>
#include "mitkXMLSerializable.h"
#include <MitkCameraCalibrationExports.h>
#include "opencv2/core.hpp"
+#include <mutex>
+
int mitkCameraIntrinsicsTest(int, char* []);
namespace mitk
{
///
/// \brief class representing camera intrinsics and related functions
///
class MITKCAMERACALIBRATION_EXPORT CameraIntrinsics: virtual public itk::Object,
virtual public mitk::XMLSerializable
{
public:
///
/// for testing purposes
///
friend int mitkCameraIntrinsicsTest(int argc, char* argv[]);
///
/// smartpointer typedefs
///
mitkClassMacroItkParent(CameraIntrinsics, itk::Object);
///
/// the static new function
///
itkFactorylessNewMacro(Self);
///
/// make a clone of this intrinsics
///
itkCloneMacro(Self);
///
/// copy information from other to this
///
void Copy(const CameraIntrinsics* other);
///
/// checks two intrinsics for equality
///
bool Equals( const CameraIntrinsics* other ) const;
///
/// \return the intrinsic parameter matrix as a 3x3 vnl matrix
///
vnl_matrix_fixed<mitk::ScalarType, 3, 3> GetVnlCameraMatrix() const;
///
/// \return the intrinsic parameter matrix as a 3x4 vnl matrix
/// (the last column only containing zeros)
///
vnl_matrix_fixed<mitk::ScalarType, 3, 4> GetVnlCameraMatrix3x4() const;
///
/// \return true if the intrinsics are set (some plausibility checks
/// may be done here)
///
bool IsValid() const;
void SetValid(bool valid);
cv::Mat GetCameraMatrix() const;
cv::Mat GetDistorsionCoeffs();
cv::Mat GetDistorsionCoeffs() const;
void ToXML(tinyxml2::XMLElement* elem) const override;
std::string ToString() const;
std::string GetString();
double GetFocalLengthX() const;
double GetFocalLengthY() const;
double GetPrincipalPointX() const;
double GetPrincipalPointY() const;
mitk::Point4D GetDistorsionCoeffsAsPoint4D() const;
mitk::Point3D GetFocalPoint() const;
mitk::Point3D GetPrincipalPoint() const;
vnl_vector_fixed<mitk::ScalarType, 2> GetFocalPointAsVnlVector() const;
vnl_vector_fixed<mitk::ScalarType, 2> GetPrincipalPointAsVnlVector() const;
///
/// set a new camera matrix utilizing a vnl matrix
///
void SetCameraMatrix( const vnl_matrix_fixed<mitk::ScalarType, 3, 3>&
_CameraMatrix );
void SetIntrinsics( const cv::Mat& _CameraMatrix
, const cv::Mat& _DistorsionCoeffs);
void SetFocalLength( double x, double y );
void SetPrincipalPoint( double x, double y );
void SetDistorsionCoeffs( double k1, double k2, double p1, double p2 );
void SetIntrinsics( const mitk::Point3D& focalPoint,
const mitk::Point3D& principalPoint,
const mitk::Point4D& distortionCoefficients);
void FromXML(const tinyxml2::XMLElement* elem) override;
void FromGMLCalibrationXML(const tinyxml2::XMLElement* elem);
std::string ToOctaveString(const std::string& varName="CameraIntrinsics");
~CameraIntrinsics() override;
protected:
CameraIntrinsics();
CameraIntrinsics(const CameraIntrinsics& other);
cv::Mat m_CameraMatrix;
cv::Mat m_DistorsionCoeffs;
bool m_Valid;
- itk::FastMutexLock::Pointer m_Mutex;
+ mutable std::mutex m_Mutex;
private:
itk::LightObject::Pointer InternalClone() const override;
};
} // namespace mitk
MITKCAMERACALIBRATION_EXPORT std::ostream& operator<<
(std::ostream& os, mitk::CameraIntrinsics::Pointer p);
#endif // mitkCameraIntrinsics_h
diff --git a/Modules/CameraCalibration/mitkEndoDebug.cpp b/Modules/CameraCalibration/mitkEndoDebug.cpp
index f86b018e70..3a62e0ce2b 100644
--- a/Modules/CameraCalibration/mitkEndoDebug.cpp
+++ b/Modules/CameraCalibration/mitkEndoDebug.cpp
@@ -1,291 +1,252 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkEndoDebug.h"
#include <itksys/SystemTools.hxx>
-#include <itkFastMutexLock.h>
-#include <itkMutexLockHolder.h>
+#include <mutex>
#include <fstream>
#include <ctime>
#include <cstdio>
namespace mitk
{
struct EndoDebugData
{
EndoDebugData()
: m_DebugEnabled(false)
, m_ShowImagesInDebug(false)
, m_ShowImagesTimeOut(false)
- , m_Mutex(itk::FastMutexLock::New())
, m_DebugImagesOutputDirectory("")
{
}
std::set<std::string> m_FilesToDebug;
std::set<std::string> m_SymbolsToDebug;
bool m_DebugEnabled;
bool m_ShowImagesInDebug;
size_t m_ShowImagesTimeOut;
std::ofstream m_Stream;
- itk::FastMutexLock::Pointer m_Mutex;
+ std::mutex m_Mutex;
std::string m_DebugImagesOutputDirectory;
};
EndoDebug::EndoDebug()
: d ( new EndoDebugData )
{
}
EndoDebug::~EndoDebug()
{
if(d->m_Stream.is_open())
d->m_Stream.close();
delete d;
}
EndoDebug& EndoDebug::GetInstance()
{
static EndoDebug instance;
return instance;
}
std::string EndoDebug::GetUniqueFileName( const std::string& dir, const std::string& ext, const std::string& prefix )
{
std::stringstream s;
s.precision( 0 );
std::string filename;
int i = 0;
while( filename.empty() || itksys::SystemTools::FileExists( (dir+"/"+filename).c_str() ) )
{
s.str("");
s << i;
filename = prefix + s.str() + "." + ext;
++i;
}
filename = dir+"/"+filename;
return filename;
}
std::string EndoDebug::GetFilenameWithoutExtension(const std::string& s)
{
return itksys::SystemTools::GetFilenameWithoutExtension( s );
}
bool EndoDebug::AddFileToDebug(const std::string& s)
{
- {
- itk::MutexLockHolder<itk::FastMutexLock> lock(*d->m_Mutex);
- std::pair<std::set<std::string>::iterator, bool> res = d->m_FilesToDebug.insert( s );
- return res.second;
- }
+ std::lock_guard<std::mutex> lock(d->m_Mutex);
+ std::pair<std::set<std::string>::iterator, bool> res = d->m_FilesToDebug.insert( s );
+ return res.second;
}
void EndoDebug::SetFilesToDebug(const std::set<std::string> &filesToDebug)
{
- {
- itk::MutexLockHolder<itk::FastMutexLock> lock(*d->m_Mutex);
- d->m_FilesToDebug = filesToDebug;
- }
+ std::lock_guard<std::mutex> lock(d->m_Mutex);
+ d->m_FilesToDebug = filesToDebug;
}
std::set<std::string> EndoDebug::GetFilesToDebug()
{
- {
- itk::MutexLockHolder<itk::FastMutexLock> lock(*d->m_Mutex);
- return d->m_FilesToDebug;
- }
+ std::lock_guard<std::mutex> lock(d->m_Mutex);
+ return d->m_FilesToDebug;
}
bool EndoDebug::AddSymbolToDebug(const std::string& symbolToDebug)
{
- {
- itk::MutexLockHolder<itk::FastMutexLock> lock(*d->m_Mutex);
- std::pair<std::set<std::string>::iterator, bool> res = d->m_SymbolsToDebug.insert( symbolToDebug );
- return res.second;
- }
+ std::lock_guard<std::mutex> lock(d->m_Mutex);
+ std::pair<std::set<std::string>::iterator, bool> res = d->m_SymbolsToDebug.insert( symbolToDebug );
+ return res.second;
}
void EndoDebug::SetSymbolsToDebug(const std::set<std::string> &symbolsToDebug)
{
- {
- itk::MutexLockHolder<itk::FastMutexLock> lock(*d->m_Mutex);
- d->m_SymbolsToDebug = symbolsToDebug;
- }
+ std::lock_guard<std::mutex> lock(d->m_Mutex);
+ d->m_SymbolsToDebug = symbolsToDebug;
}
std::set<std::string> EndoDebug::GetSymbolsToDebug()
{
- {
- itk::MutexLockHolder<itk::FastMutexLock> lock(*d->m_Mutex);
- return d->m_SymbolsToDebug;
- }
+ std::lock_guard<std::mutex> lock(d->m_Mutex);
+ return d->m_SymbolsToDebug;
}
bool EndoDebug::DebugSymbol(const std::string& s)
{
- {
- itk::MutexLockHolder<itk::FastMutexLock> lock(*d->m_Mutex);
- return d->m_SymbolsToDebug.find(s)
- != d->m_SymbolsToDebug.end();
- }
+ std::lock_guard<std::mutex> lock(d->m_Mutex);
+ return d->m_SymbolsToDebug.find(s)
+ != d->m_SymbolsToDebug.end();
}
bool EndoDebug::DebugFile(const std::string& s)
{
std::string filename = GetFilenameWithoutExtension(s);
- {
- itk::MutexLockHolder<itk::FastMutexLock> lock(*d->m_Mutex);
- return d->m_FilesToDebug.find(filename)
- != d->m_FilesToDebug.end();
- }
+ std::lock_guard<std::mutex> lock(d->m_Mutex);
+ return d->m_FilesToDebug.find(filename)
+ != d->m_FilesToDebug.end();
}
bool EndoDebug::Debug( const std::string& fileToDebug, const std::string& symbol )
{
bool debug = false;
{
bool debugEnabled = false;
size_t filesSize = 0;
size_t symbolsSize = 0;
bool symbolFound = false;
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*d->m_Mutex);
+ std::lock_guard<std::mutex> lock(d->m_Mutex);
debugEnabled = d->m_DebugEnabled;
filesSize = d->m_FilesToDebug.size();
symbolsSize = d->m_SymbolsToDebug.size();
symbolFound = d->m_SymbolsToDebug.find(symbol) != d->m_SymbolsToDebug.end();
}
if( debugEnabled )
{
if( filesSize == 0 )
debug = true;
else
debug = DebugFile(fileToDebug);
// ok debug is determined so far, now check if symbol set
if( symbolsSize > 0 )
{
debug = symbolFound;
}
else
{
// do not show symbol debug output if no symbols are set at all
if( !symbol.empty() )
debug = false;
}
}
}
return debug;
}
void EndoDebug::SetDebugEnabled(bool _DebugEnabled)
{
- {
- itk::MutexLockHolder<itk::FastMutexLock> lock(*d->m_Mutex);
- d->m_DebugEnabled = _DebugEnabled;
- }
+ std::lock_guard<std::mutex> lock(d->m_Mutex);
+ d->m_DebugEnabled = _DebugEnabled;
}
void EndoDebug::SetDebugImagesOutputDirectory(const std::string& _DebugImagesOutputDirectory)
{
- {
- itk::MutexLockHolder<itk::FastMutexLock> lock(*d->m_Mutex);
- d->m_DebugImagesOutputDirectory = _DebugImagesOutputDirectory;
- }
-
+ std::lock_guard<std::mutex> lock(d->m_Mutex);
+ d->m_DebugImagesOutputDirectory = _DebugImagesOutputDirectory;
}
bool EndoDebug::GetDebugEnabled()
{
- {
- itk::MutexLockHolder<itk::FastMutexLock> lock(*d->m_Mutex);
- return d->m_DebugEnabled;
- }
+ std::lock_guard<std::mutex> lock(d->m_Mutex);
+ return d->m_DebugEnabled;
}
void EndoDebug::SetShowImagesInDebug(bool _ShowImagesInDebug)
{
- {
- itk::MutexLockHolder<itk::FastMutexLock> lock(*d->m_Mutex);
- d->m_ShowImagesInDebug = _ShowImagesInDebug;
- }
+ std::lock_guard<std::mutex> lock(d->m_Mutex);
+ d->m_ShowImagesInDebug = _ShowImagesInDebug;
}
bool EndoDebug::GetShowImagesInDebug()
{
- {
- itk::MutexLockHolder<itk::FastMutexLock> lock(*d->m_Mutex);
- return d->m_ShowImagesInDebug;
- }
+ std::lock_guard<std::mutex> lock(d->m_Mutex);
+ return d->m_ShowImagesInDebug;
}
void EndoDebug::SetShowImagesTimeOut(size_t _ShowImagesTimeOut)
{
- {
- itk::MutexLockHolder<itk::FastMutexLock> lock(*d->m_Mutex);
- d->m_ShowImagesTimeOut = _ShowImagesTimeOut;
- }
+ std::lock_guard<std::mutex> lock(d->m_Mutex);
+ d->m_ShowImagesTimeOut = _ShowImagesTimeOut;
}
std::string EndoDebug::GetDebugImagesOutputDirectory() const
{
- {
- itk::MutexLockHolder<itk::FastMutexLock> lock(*d->m_Mutex);
- return d->m_DebugImagesOutputDirectory;
- }
+ std::lock_guard<std::mutex> lock(d->m_Mutex);
+ return d->m_DebugImagesOutputDirectory;
}
size_t EndoDebug::GetShowImagesTimeOut()
{
- {
- itk::MutexLockHolder<itk::FastMutexLock> lock(*d->m_Mutex);
- return d->m_ShowImagesTimeOut;
- }
+ std::lock_guard<std::mutex> lock(d->m_Mutex);
+ return d->m_ShowImagesTimeOut;
}
void EndoDebug::SetLogFile( const std::string& file )
{
- {
- itk::MutexLockHolder<itk::FastMutexLock> lock(*d->m_Mutex);
- d->m_Stream.open ( file.c_str(), std::ios::out | std::ios::app);
- }
+ std::lock_guard<std::mutex> lock(d->m_Mutex);
+ d->m_Stream.open ( file.c_str(), std::ios::out | std::ios::app);
}
void EndoDebug::ShowMessage( const std::string& message )
{
+ std::lock_guard<std::mutex> lock(d->m_Mutex);
+ if(d->m_Stream.is_open())
{
- itk::MutexLockHolder<itk::FastMutexLock> lock(*d->m_Mutex);
- if(d->m_Stream.is_open())
- {
- char *timestr;
- struct tm *newtime;
- time_t aclock;
- time(&aclock);
- newtime = localtime(&aclock);
- timestr = asctime(newtime);
-
- d->m_Stream << timestr << ", " << message;
- }
- else
- std::cout << message << std::flush;
+ char *timestr;
+ struct tm *newtime;
+ time_t aclock;
+ time(&aclock);
+ newtime = localtime(&aclock);
+ timestr = asctime(newtime);
+
+ d->m_Stream << timestr << ", " << message;
}
+ else
+ std::cout << message << std::flush;
}
}
diff --git a/Modules/CameraCalibration/mitkTransform.cpp b/Modules/CameraCalibration/mitkTransform.cpp
index 0a65753afb..c0966266d8 100644
--- a/Modules/CameraCalibration/mitkTransform.cpp
+++ b/Modules/CameraCalibration/mitkTransform.cpp
@@ -1,750 +1,750 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkTransform.h"
#include <fstream>
#include <vnl/vnl_inverse.h>
#include <mitkVnlVectorFixedCaster.h>
#include <mitkVnlVectorFromCvMat.h>
#include <mitkVnlMatrixFromCvMat.h>
#include <mitkCvMatFromVnlVector.h>
#include <tinyxml2.h>
namespace mitk
{
// DO NOT CHANGE THE VALUES OF THESE CONSTANTS!!
const std::string Transform::UNKNOWN_TYPE = "Unknown type";
const std::string Transform::ENDOSCOPE_SCOPE_TOOL = "Endoscope scope tool";
const std::string Transform::ENDOSCOPE_CAM_TOOL = "Endoscope camera tool";
const std::string Transform::CHESSBOARD_TOOL = "Chessboard tool";
const std::string Transform::POINTER_TOOL = "Pointer tool";
const std::string Transform::POINTER_TO_CHESSBOARD_ORIGIN = "Pointer to chessboard origin";
const std::string Transform::POINTER_TO_CHESSBOARD_X_SUPPORT_POINT = "Pointer to chessboard X support origin";
const std::string Transform::POINTER_TO_CHESSBOARD_Y_SUPPORT_POINT = "Pointer to chessboard Y support origin";
const std::string Transform::BOARD_TO_BOARD_TOOL = "Board to board tool";
const std::string Transform::REFERENCE_CAMERA_TRANSFORM = "Reference camera transform";
const std::string Transform::REFERENCE_SCOPE_TRANSFORM = "Reference scope transform";
const std::string Transform::EYE_TO_HAND_TRANSFORM = "Eye to hand transform";
const std::string Transform::CAMERA_EXTRINSICS = "Camera extrinsics";
Transform::Transform()
: m_NavData(mitk::NavigationData::New()), m_Type( UNKNOWN_TYPE )
{
vnl_matrix_fixed<mitk::ScalarType, 3, 3> rot;
rot.set_identity();
this->SetRotation( rot );
}
Transform::Transform(const mitk::NavigationData* nd)
: m_NavData(mitk::NavigationData::New()), m_Type( UNKNOWN_TYPE )
{
m_NavData->Graft(nd);
}
Transform::Transform(const std::string& s)
: m_NavData(mitk::NavigationData::New()), m_Type( s )
{
vnl_matrix_fixed<mitk::ScalarType, 3, 3> rot;
rot.set_identity();
this->SetRotation( rot );
}
void Transform::Copy(const mitk::NavigationData* nd)
{
(const_cast<mitk::NavigationData*>(m_NavData.GetPointer()))->Graft(nd);
}
void Transform::Concatenate( mitk::Transform* transform )
{
vnl_matrix_fixed<mitk::ScalarType, 4, 4> mat = transform->GetMatrix();
mat = mat * this->GetMatrix(); //
this->SetMatrix( mat );
}
void Transform::Concatenate( const vnl_matrix_fixed<mitk::ScalarType, 4, 4>&
transform )
{
Transform::Pointer t = Transform::New();
t->SetMatrix( transform );
this->Concatenate( t );
}
void Transform::Concatenate( const vtkMatrix4x4* transform )
{
Transform::Pointer t = Transform::New();
t->SetMatrix( transform );
this->Concatenate( t );
}
void Transform::Reset()
{
mitk::NavigationData::Pointer nd
= NavigationData::New();
this->Copy( nd );
}
void Transform::SetOrientation(
const vnl_quaternion<mitk::ScalarType>& orientation)
{
m_NavData->SetOrientation(orientation);
this->Modified();
}
void Transform::SetTranslation( const vnl_vector_fixed<mitk::ScalarType, 3>&
transl)
{
mitk::Point3D p;
for(unsigned int i=0; i<3; ++i)
p[i] = transl[i];
m_NavData->SetPosition(p);
this->Modified();
}
void Transform::SetTranslation( float* array )
{
vnl_vector_fixed<mitk::ScalarType, 3> vec;
for(unsigned int i=0; i<vec.size(); i++)
vec(i) = array[i];
this->SetTranslation( vec );
}
void Transform::SetRotation( float* array )
{
vnl_matrix_fixed<mitk::ScalarType, 3, 3> mat;
unsigned int row = 0;
unsigned int col = 0;
for(unsigned int i=0; i<mat.rows()*mat.cols(); i++)
{
if( i > 0 && i % 3 == 0 )
{
++row;
col = 0;
}
mat(row,col) = array[i];
++col;
}
this->SetRotation( mat );
}
void Transform::SetOrientation( const vnl_quaternion<float>& orientation)
{
vnl_vector_fixed<mitk::ScalarType, 4> qvec;
VnlVectorFixedCaster<float, mitk::ScalarType, 4> caster( &orientation, &qvec );
caster.Update();
mitk::Quaternion p( qvec );
this->SetOrientation( p );
}
vnl_vector_fixed<double, 3> Transform::GetVnlDoubleTranslation() const
{
vnl_vector_fixed<mitk::ScalarType, 3> vecFloat = this->GetVnlTranslation();
vnl_vector_fixed<double, 3> vecDouble;
VnlVectorFixedCaster<mitk::ScalarType, double, 3> caster( &vecFloat, &vecDouble );
caster.Update();
return vecDouble;
}
void Transform::SetTranslation( const vnl_vector<double>& transl)
{
vnl_vector_fixed<double, 3> dTransl(transl);
vnl_vector_fixed<mitk::ScalarType, 3> fTransl;
VnlVectorFixedCaster<double, mitk::ScalarType, 3> caster( &dTransl, &fTransl );
caster.Update();
this->SetTranslation( fTransl );
}
vnl_quaternion<double> Transform::GetVnlDoubleQuaternion() const
{
mitk::Quaternion fOrientation = this->GetOrientation();
vnl_quaternion<double> dOrientation;
VnlVectorFixedCaster<mitk::ScalarType, double, 4> caster( &fOrientation, &dOrientation );
caster.Update();
return dOrientation;
}
void Transform::FromCSVFile(const std::string& file)
{
std::ifstream csvFile (file.c_str());
endoAssert ( csvFile.fail() == false );
mitk::Transform::Pointer transform = mitk::Transform::New();
vnl_matrix_fixed<mitk::ScalarType, 4, 4> mat;
std::string line;
mitk::ScalarType d = 0.0f;
int row=0,column = 0;
while (std::getline (csvFile, line))
{
std::istringstream linestream(line);
std::string item;
column = 0;
while (std::getline (linestream, item, ','))
{
std::istringstream number;
number.str(item);
number >> d;
mat(row, column) = d;
++column;
}
++row;
}
endoAssert( row == 4 && column == 4 );
transform->SetMatrix( mat );
this->SetNavigationData( transform->GetNavigationData() );
// modified is called in SetNavigationData
}
std::string Transform::ToCSVString() const
{
std::ostringstream s; s.precision(12);
vnl_matrix_fixed<mitk::ScalarType, 4, 4> mat
= this->GetMatrix();
for( unsigned int j=0; j<mat.rows(); ++j )
{
for( unsigned int k=0; k<mat.cols(); ++k )
{
s << mat(j,k);
if(k+1<mat.cols())
s << ",";
}
if(j+1<mat.rows())
s << std::endl;
}
return s.str();
}
std::string Transform::ToMatlabString(const std::string& varname
, bool printLastRow) const
{
std::ostringstream s; s.precision(12);
vnl_matrix_fixed<mitk::ScalarType, 4, 4> mat
= this->GetMatrix();
s << varname << " = [";
for( unsigned int j=0; j<mat.rows(); ++j )
{
if( !printLastRow && j+1 == mat.rows() )
break;
for( unsigned int k=0; k<mat.cols(); ++k )
{
s << mat(j,k) << " ";
}
s << ";";
}
s << "];" << std::endl;
return s.str();
}
void Transform::Copy( const mitk::Transform* transform )
{
m_NavData->Graft(transform->GetNavigationData());
m_Type = transform->GetType();
}
mitk::Transform::Pointer Transform::Clone() const
{
Transform::Pointer copy = Transform::New();
copy->Copy( this );
return copy;
}
void Transform::SetMatrix( const vtkMatrix4x4* mat)
{
vnl_matrix_fixed<mitk::ScalarType, 4, 4> vnlMat;
for(unsigned int i=0; i<4; ++i)
for(unsigned int j=0; j<4; ++j)
vnlMat(i,j) = mat->GetElement(i, j);
this->SetMatrix( vnlMat );
}
void Transform::ToCSVFile(const std::string& file) const
{
std::ofstream csvFile;
csvFile.open(file.c_str());
endoAssert ( csvFile.fail() == false );
csvFile << this->ToCSVString();
csvFile.close();
}
void Transform::ToMatlabFile(const std::string& file
, const std::string& varname) const
{
std::ofstream csvFile;
csvFile.open(file.c_str());
endoAssert ( csvFile.fail() == false );
csvFile << this->ToMatlabString(varname);
csvFile.close();
}
void Transform::SetNavigationData( const mitk::NavigationData* naviData )
{
endoAssert( naviData != nullptr );
m_NavData->Graft( naviData );
this->Modified();
}
void Transform::SetRotation( vnl_matrix_fixed<mitk::ScalarType, 3, 3>& mat)
{
this->m_NavData->SetOrientation( mitk::Quaternion(mat) );
this->Modified();
}
void Transform::SetRotation( vnl_matrix<mitk::ScalarType>& mat)
{
vnl_matrix_fixed<mitk::ScalarType, 3, 3> tmp(mat);
this->SetRotation( tmp );
}
void Transform::SetPosition( const mitk::Point3D& transl)
{
this->SetTranslation( transl.GetVnlVector() );
}
void Transform::SetTranslation( double array[3] )
{
mitk::Point3D p;
for(unsigned int i = 0; i < 3; ++i)
p.SetElement(i, array[i]);
this->SetTranslation( p.GetVnlVector() );
}
void Transform::SetRotation( double array[3][3] )
{
vnl_matrix_fixed<mitk::ScalarType, 3, 3> mat;
for(unsigned int i = 0; i < 3; ++i)
for(unsigned int j = 0; j < 3; ++j)
mat(i, j) = array[i][j];
this->SetRotation( mat );
}
void Transform::Invert()
{
vnl_matrix_fixed<mitk::ScalarType, 4, 4> tmp(this->GetMatrix());
this->SetMatrix( vnl_inverse( tmp ) );
}
void Transform::SetMatrix(
const vnl_matrix_fixed<mitk::ScalarType, 4, 4>& mat)
{
// set translation first
- vnl_vector<mitk::ScalarType> transl = mat.get_column(3);
+ vnl_vector<mitk::ScalarType> transl = mat.get_column(3).as_ref();
mitk::Point3D p;
for(unsigned int i=0; i<3; ++i)
p[i] = transl[i];
m_NavData->SetPosition(p);
// set rotation
vnl_matrix_fixed<mitk::ScalarType, 3, 3> rotMatFixed(
mat.extract(3,3));
this->SetRotation(rotMatFixed);
}
bool Transform::IsValid() const
{
return m_NavData->IsDataValid();
}
void Transform::SetTranslation( const cv::Mat& transl)
{
vnl_vector<mitk::ScalarType> vec(3);
VnlVectorFromCvMat<mitk::ScalarType> _VnlVectorFromCvMat( &transl, &vec );
_VnlVectorFromCvMat.Update();
this->SetTranslation( vnl_vector_fixed<mitk::ScalarType, 3>( vec ) );
}
void Transform::SetRotation( const cv::Mat& mat )
{
vnl_matrix<mitk::ScalarType> vnlMat(3, 3);
VnlMatrixFromCvMat<mitk::ScalarType> _VnlMatrixFromCvMat( &mat, &vnlMat );
_VnlMatrixFromCvMat.Update();
vnl_matrix_fixed<mitk::ScalarType, 3, 3> vnlMatFixed(vnlMat);
this->SetRotation(vnlMatFixed);
}
void Transform::SetRotationVector( const cv::Mat& rotVec )
{
cv::Mat rotMat;
cv::Rodrigues( rotVec, rotMat );
vnl_matrix<mitk::ScalarType> vnlMat(3, 3);
VnlMatrixFromCvMat<mitk::ScalarType> _VnlMatrixFromCvMat( &rotMat, &vnlMat );
_VnlMatrixFromCvMat.Update();
vnl_matrix_fixed<mitk::ScalarType, 3, 3> vnlMatFixed(vnlMat);
this->SetRotation( vnlMatFixed );
}
//# getter
mitk::NavigationData::Pointer Transform::GetNavigationData() const
{
return m_NavData;
}
mitk::Point3D Transform::GetTranslation() const
{
return m_NavData->GetPosition();
}
mitk::Point3D Transform::GetPosition() const
{
return m_NavData->GetPosition();
}
mitk::Quaternion Transform::GetOrientation() const
{
return m_NavData->GetOrientation();
}
void Transform::GetMatrix(vtkMatrix4x4* matrix) const
{
vnl_matrix_fixed<mitk::ScalarType, 4, 4> vnlMat = this->GetMatrix();
for(unsigned int i=0; i<vnlMat.rows(); ++i)
for(unsigned int j=0; j<vnlMat.cols(); ++j)
matrix->SetElement(i,j, vnlMat(i,j));
}
void Transform::GetVtkOpenGlMatrix(vtkMatrix4x4* matrix) const
{
vnl_matrix<mitk::ScalarType> vnlRotation
= this->GetVnlRotationMatrix().as_matrix();
// normalize rows of rotation matrix
vnlRotation.normalize_rows();
vnl_matrix<mitk::ScalarType> vnlInverseRotation(3,3);
// invert rotation
- vnlInverseRotation = vnl_matrix_inverse<mitk::ScalarType>(vnlRotation);
+ vnlInverseRotation = vnl_matrix_inverse<mitk::ScalarType>(vnlRotation.as_ref()).as_matrix();
vnl_vector<mitk::ScalarType> vnlTranslation
= this->GetPosition().GetVnlVector();
// rotate translation vector by inverse rotation P = P'
vnlTranslation = vnlInverseRotation * vnlTranslation;
vnlTranslation *= -1; // save -P'
// set position
mitk::Transform::Pointer tmp = mitk::Transform::New();
tmp->SetTranslation( vnlTranslation );
tmp->SetRotation( vnlRotation );
tmp->GetMatrix(matrix);
}
mitk::Point3D Transform::TransformPoint(mitk::Point3D point) const
{
itk::Matrix<mitk::ScalarType,3,3> R(GetVnlRotationMatrix());
itk::Point<mitk::ScalarType,3> pointR = (R * point);
mitk::Point3D retPoint = pointR;
retPoint[0] = pointR[0] + GetPosition()[0];
retPoint[1] = pointR[1] + GetPosition()[1];
retPoint[2] = pointR[2] + GetPosition()[2];
return retPoint;
}
//# cv getter
cv::Mat Transform::GetCvTranslation() const
{
cv::Mat mat;
vnl_vector<mitk::ScalarType> vec = this->GetVnlTranslation().as_vector();
endodebugvar( vec )
CvMatFromVnlVector<mitk::ScalarType> _CvMatFromVnlVector(&vec, &mat);
_CvMatFromVnlVector.Update();
return mat;
}
cv::Mat Transform::GetCvRotationMatrix() const
{
cv::Mat mat;
vnl_matrix<mitk::ScalarType> vec = this->GetVnlRotationMatrix().as_matrix();
endodebugvar( vec )
CvMatFromVnlMatrix<mitk::ScalarType> _CvMatFromVnlMatrix(&vec, &mat);
_CvMatFromVnlMatrix.Update();
return mat;
}
cv::Mat Transform::GetCvMatrix() const
{
cv::Mat mat;
vnl_matrix<mitk::ScalarType> vec = this->GetMatrix().as_matrix();
CvMatFromVnlMatrix<mitk::ScalarType> _CvMatFromVnlMatrix(&vec, &mat);
_CvMatFromVnlMatrix.Update();
return mat;
}
cv::Mat Transform::GetCvRotationVector() const
{
cv::Mat rotVec(3,1,cv::DataType<mitk::ScalarType>::type);
cv::Rodrigues( this->GetCvRotationMatrix(), rotVec );
return rotVec;
}
//# vnl getter
vnl_vector_fixed<mitk::ScalarType, 3> Transform::GetVnlTranslation() const
{
vnl_vector_fixed<mitk::ScalarType, 3> vec(m_NavData->GetPosition()
.GetVnlVector());
return vec;
}
vnl_matrix_fixed<mitk::ScalarType, 3, 3> Transform::GetVnlRotationMatrix() const
{
return m_NavData->GetOrientation().rotation_matrix_transpose();
}
vnl_matrix_fixed<double, 4, 4> Transform::GetVnlDoubleMatrix() const
{
vnl_matrix_fixed<mitk::ScalarType, 4, 4> mat = this->GetMatrix();
vnl_matrix_fixed<double, 4, 4> doubleMat;
for(unsigned int i=0; i<mat.rows(); ++i)
for(unsigned int j=0; j<mat.cols(); ++j)
doubleMat(i,j) = static_cast<double>( mat(i,j) );
return doubleMat;
}
vnl_matrix_fixed<mitk::ScalarType, 4, 4> Transform::GetMatrix()
const
{
vnl_vector_fixed<mitk::ScalarType, 3> transl = this->GetVnlTranslation();
vnl_matrix_fixed<mitk::ScalarType, 3, 3> rot = this->GetVnlRotationMatrix();
vnl_matrix_fixed<mitk::ScalarType, 4, 4> homMat;
homMat.set_identity();
//std::cout << homMat << std::endl;
for(unsigned int i=0; i<rot.rows(); ++i)
for(unsigned int j=0; j<rot.cols(); ++j)
homMat(i,j) = rot(i,j);
for(unsigned int i=0; i<transl.size(); ++i)
homMat(i,3) = transl[i];
return homMat;
}
void Transform::TransposeRotation()
{
vnl_matrix_fixed<mitk::ScalarType, 3, 3> rotMat = this->GetVnlRotationMatrix().transpose();
this->SetRotation( rotMat );
}
void Transform::SetValid( bool valid )
{
if( m_NavData->IsDataValid() == valid )
return;
m_NavData->SetDataValid( valid );
this->Modified();
}
std::string mitk::Transform::ToString() const
{
std::ostringstream s; s.precision(12);
mitk::NavigationData::PositionType position;
position.Fill(0.0);
position = m_NavData->GetPosition();
mitk::NavigationData::OrientationType orientation(0.0, 0.0, 0.0, 0.0);
orientation = m_NavData->GetOrientation();
s << "Translation: [" << position[0] << ", " << position[1] << ", "
<< position[2] << "]";
s << ", orientation: [" << orientation[0] << ", " << orientation[1] << ", "
<< orientation[2] << ", " << orientation[3] << "]";
s << ", valid: [" << (this->IsValid()? "true": "false") << "]";
return s.str();
}
void mitk::Transform::ToXML(tinyxml2::XMLElement* elem) const
{
std::string value = elem->Value() != nullptr ? elem->Value() : "";
if(value.empty())
elem->SetValue(this->GetNameOfClass());
mitk::NavigationData::PositionType position;
position.Fill(0.0);
position = m_NavData->GetPosition();
mitk::NavigationData::OrientationType orientation(0.0, 0.0, 0.0, 0.0);
orientation = m_NavData->GetOrientation();
mitk::NavigationData::CovarianceMatrixType matrix;
matrix.SetIdentity();
matrix = m_NavData->GetCovErrorMatrix();
bool hasPosition = true;
hasPosition = m_NavData->GetHasPosition();
bool hasOrientation = true;
hasOrientation = m_NavData->GetHasOrientation();
bool dataValid = false;
dataValid = m_NavData->IsDataValid();
mitk::NavigationData::TimeStampType timestamp=0.0;
elem->SetAttribute("Type", m_Type.c_str());
elem->SetAttribute("Time", timestamp);
elem->SetAttribute("X", position[0]);
elem->SetAttribute("Y", position[1]);
elem->SetAttribute("Z", position[2]);
elem->SetAttribute("QX", orientation[0]);
elem->SetAttribute("QY", orientation[1]);
elem->SetAttribute("QZ", orientation[2]);
elem->SetAttribute("QR", orientation[3]);
elem->SetAttribute("C00", matrix[0][0]);
elem->SetAttribute("C01", matrix[0][1]);
elem->SetAttribute("C02", matrix[0][2]);
elem->SetAttribute("C03", matrix[0][3]);
elem->SetAttribute("C04", matrix[0][4]);
elem->SetAttribute("C05", matrix[0][5]);
elem->SetAttribute("C10", matrix[1][0]);
elem->SetAttribute("C11", matrix[1][1]);
elem->SetAttribute("C12", matrix[1][2]);
elem->SetAttribute("C13", matrix[1][3]);
elem->SetAttribute("C14", matrix[1][4]);
elem->SetAttribute("C15", matrix[1][5]);
if (dataValid)
elem->SetAttribute("Valid",1);
else
elem->SetAttribute("Valid",0);
if (hasOrientation)
elem->SetAttribute("hO",1);
else
elem->SetAttribute("hO",0);
if (hasPosition)
elem->SetAttribute("hP",1);
else
elem->SetAttribute("hP",0);
}
void mitk::Transform::FromXML(const tinyxml2::XMLElement* elem)
{
assert(elem);
mitk::NavigationData::Pointer nd = mitk::NavigationData::New();
mitk::NavigationData::PositionType position;
mitk::NavigationData::OrientationType orientation(0.0,0.0,0.0,0.0);
mitk::NavigationData::TimeStampType timestamp = -1;
mitk::NavigationData::CovarianceMatrixType matrix;
bool hasPosition = true;
bool hasOrientation = true;
bool dataValid = false;
position.Fill(0.0);
matrix.SetIdentity();
const char* typeC = elem->Attribute("Type");
std::string type = nullptr == typeC
? Transform::UNKNOWN_TYPE
: typeC;
elem->QueryDoubleAttribute("Time",&timestamp);
// position and orientation is mandatory!
if(elem->QueryDoubleAttribute("X", &position[0]) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("No X position found in xml");
if(elem->QueryDoubleAttribute("Y", &position[1]) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("No Y position found in xml");
if(elem->QueryDoubleAttribute("Z", &position[2]) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("No Z position found in xml");
if(elem->QueryDoubleAttribute("QX", &orientation[0]) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("No QX orientation found in xml");
if(elem->QueryDoubleAttribute("QY", &orientation[1]) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("No QY orientation found in xml");
if(elem->QueryDoubleAttribute("QZ", &orientation[2]) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("No QZ orientation found in xml");
if(elem->QueryDoubleAttribute("QR", &orientation[3]) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("No QR orientation found in xml");
elem->QueryDoubleAttribute("C00", &matrix[0][0]);
elem->QueryDoubleAttribute("C01", &matrix[0][1]);
elem->QueryDoubleAttribute("C02", &matrix[0][2]);
elem->QueryDoubleAttribute("C03", &matrix[0][3]);
elem->QueryDoubleAttribute("C04", &matrix[0][4]);
elem->QueryDoubleAttribute("C05", &matrix[0][5]);
elem->QueryDoubleAttribute("C10", &matrix[1][0]);
elem->QueryDoubleAttribute("C11", &matrix[1][1]);
elem->QueryDoubleAttribute("C12", &matrix[1][2]);
elem->QueryDoubleAttribute("C13", &matrix[1][3]);
elem->QueryDoubleAttribute("C14", &matrix[1][4]);
elem->QueryDoubleAttribute("C15", &matrix[1][5]);
int tmpval = 0;
elem->QueryIntAttribute("Valid", &tmpval);
if (tmpval == 0)
dataValid = false;
else
dataValid = true;
tmpval = 0;
elem->QueryIntAttribute("hO", &tmpval);
if (tmpval == 0)
hasOrientation = false;
else
hasOrientation = true;
tmpval = 0;
elem->QueryIntAttribute("hP", &tmpval);
if (tmpval == 0)
hasPosition = false;
else
hasPosition = true;
nd->SetIGTTimeStamp(timestamp);
nd->SetPosition(position);
nd->SetOrientation(orientation);
nd->SetCovErrorMatrix(matrix);
nd->SetDataValid(dataValid);
nd->SetHasOrientation(hasOrientation);
nd->SetHasPosition(hasPosition);
m_NavData = nd;
m_Type = type;
this->Modified();
}
} // namespace mitk
std::ostream& operator<< (std::ostream& os, mitk::Transform::Pointer p)
{
os << p->ToString();
return os;
}
diff --git a/Modules/Classification/CLLibSVM/src/svm.cpp b/Modules/Classification/CLLibSVM/src/svm.cpp
index b40300d389..d820dc2df2 100644
--- a/Modules/Classification/CLLibSVM/src/svm.cpp
+++ b/Modules/Classification/CLLibSVM/src/svm.cpp
@@ -1,3293 +1,3291 @@
/*============================================================================
libsvm
Copyright (c) 2000-2019 Chih-Chung Chang and Chih-Jen Lin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither name of copyright holders nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
============================================================================*/
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cfloat>
#include <cstring>
#include <cstdarg>
#include <climits>
#include <clocale>
#include <mitkLocaleSwitch.h>
#include "svm.h"
int libsvm_version = LIBSVM_VERSION;
typedef float Qfloat;
typedef signed char schar;
#ifndef min
template <class T> static inline T min(T x,T y) { return (x<y)?x:y; }
#endif
#ifndef max
template <class T> static inline T max(T x,T y) { return (x>y)?x:y; }
#endif
template <class T> static inline void swap(T& x, T& y) { T t=x; x=y; y=t; }
template <class S, class T> static inline void clone(T*& dst, S* src, int n)
{
dst = new T[n];
memcpy((void *)dst,(void *)src,sizeof(T)*n);
}
static inline double powi(double base, int times)
{
double tmp = base, ret = 1.0;
for(int t=times; t>0; t/=2)
{
if(t%2==1) ret*=tmp;
tmp = tmp * tmp;
}
return ret;
}
#define INF HUGE_VAL
#define TAU 1e-12
#define Malloc(type,n) (type *)malloc((n)*sizeof(type))
static void print_string_stdout(const char *s)
{
fputs(s,stdout);
fflush(stdout);
}
static void (*svm_print_string) (const char *) = &print_string_stdout;
#if 1
static void info(const char *fmt,...)
{
char buf[BUFSIZ];
va_list ap;
va_start(ap,fmt);
vsprintf(buf,fmt,ap);
va_end(ap);
(*svm_print_string)(buf);
}
#else
static void info(const char *fmt,...) {}
#endif
//
// Kernel Cache
//
// l is the number of total data items
// size is the cache size limit in bytes
//
class Cache
{
public:
Cache(int l,long int size);
~Cache();
// request data [0,len)
// return some position p where [p,len) need to be filled
// (p >= len if nothing needs to be filled)
int get_data(const int index, Qfloat **data, int len);
void swap_index(int i, int j);
private:
int l;
long int size;
struct head_t
{
head_t *prev, *next; // a circular list
Qfloat *data;
int len; // data[0,len) is cached in this entry
};
head_t *head;
head_t lru_head;
void lru_delete(head_t *h);
void lru_insert(head_t *h);
};
Cache::Cache(int l_,long int size_):l(l_),size(size_)
{
head = (head_t *)calloc(l,sizeof(head_t)); // initialized to 0
size /= sizeof(Qfloat);
size -= l * sizeof(head_t) / sizeof(Qfloat);
size = max(size, 2 * (long int) l); // cache must be large enough for two columns
lru_head.next = lru_head.prev = &lru_head;
}
Cache::~Cache()
{
for(head_t *h = lru_head.next; h != &lru_head; h=h->next)
free(h->data);
free(head);
}
void Cache::lru_delete(head_t *h)
{
// delete from current location
h->prev->next = h->next;
h->next->prev = h->prev;
}
void Cache::lru_insert(head_t *h)
{
// insert to last position
h->next = &lru_head;
h->prev = lru_head.prev;
h->prev->next = h;
h->next->prev = h;
}
int Cache::get_data(const int index, Qfloat **data, int len)
{
head_t *h = &head[index];
if(h->len) lru_delete(h);
int more = len - h->len;
if(more > 0)
{
// free old space
while(size < more)
{
head_t *old = lru_head.next;
lru_delete(old);
free(old->data);
size += old->len;
old->data = nullptr;
old->len = 0;
}
// allocate new space
h->data = (Qfloat *)realloc(h->data,sizeof(Qfloat)*len);
size -= more;
swap(h->len,len);
}
lru_insert(h);
*data = h->data;
return len;
}
void Cache::swap_index(int i, int j)
{
if(i==j) return;
if(head[i].len) lru_delete(&head[i]);
if(head[j].len) lru_delete(&head[j]);
swap(head[i].data,head[j].data);
swap(head[i].len,head[j].len);
if(head[i].len) lru_insert(&head[i]);
if(head[j].len) lru_insert(&head[j]);
if(i>j) swap(i,j);
for(head_t *h = lru_head.next; h!=&lru_head; h=h->next)
{
if(h->len > i)
{
if(h->len > j)
swap(h->data[i],h->data[j]);
else
{
// give up
lru_delete(h);
free(h->data);
size += h->len;
h->data = nullptr;
h->len = 0;
}
}
}
}
//
// Kernel evaluation
//
// the static method k_function is for doing single kernel evaluation
// the constructor of Kernel prepares to calculate the l*l kernel matrix
// the member function get_Q is for getting one column from the Q Matrix
//
class QMatrix {
public:
virtual Qfloat *get_Q(int column, int len) const = 0;
virtual double *get_QD() const = 0;
virtual void swap_index(int i, int j) const = 0;
virtual ~QMatrix() {}
};
class Kernel: public QMatrix {
public:
Kernel(int l, svm_node * const * x, const svm_parameter& param);
~Kernel() override;
static double k_function(const svm_node *x, const svm_node *y,
const svm_parameter& param);
Qfloat *get_Q(int column, int len) const override = 0;
double *get_QD() const override = 0;
void swap_index(int i, int j) const override // no so const...
{
swap(x[i],x[j]);
if(x_square) swap(x_square[i],x_square[j]);
}
protected:
double (Kernel::*kernel_function)(int i, int j) const;
private:
const svm_node **x;
double *x_square;
// svm_parameter
const int kernel_type;
const int degree;
const double gamma;
const double coef0;
static double dot(const svm_node *px, const svm_node *py);
double kernel_linear(int i, int j) const
{
return dot(x[i],x[j]);
}
double kernel_poly(int i, int j) const
{
return powi(gamma*dot(x[i],x[j])+coef0,degree);
}
double kernel_rbf(int i, int j) const
{
return exp(-gamma*(x_square[i]+x_square[j]-2*dot(x[i],x[j])));
}
double kernel_sigmoid(int i, int j) const
{
return tanh(gamma*dot(x[i],x[j])+coef0);
}
double kernel_precomputed(int i, int j) const
{
return x[i][(int)(x[j][0].value)].value;
}
};
Kernel::Kernel(int l, svm_node * const * x_, const svm_parameter& param)
:kernel_type(param.kernel_type), degree(param.degree),
gamma(param.gamma), coef0(param.coef0)
{
switch(kernel_type)
{
case LINEAR:
kernel_function = &Kernel::kernel_linear;
break;
case POLY:
kernel_function = &Kernel::kernel_poly;
break;
case RBF:
kernel_function = &Kernel::kernel_rbf;
break;
case SIGMOID:
kernel_function = &Kernel::kernel_sigmoid;
break;
case PRECOMPUTED:
kernel_function = &Kernel::kernel_precomputed;
break;
}
clone(x,x_,l);
if(kernel_type == RBF)
{
x_square = new double[l];
for(int i=0;i<l;i++)
x_square[i] = dot(x[i],x[i]);
}
else
x_square = nullptr;
}
Kernel::~Kernel()
{
delete[] x;
delete[] x_square;
}
double Kernel::dot(const svm_node *px, const svm_node *py)
{
double sum = 0;
while(px->index != -1 && py->index != -1)
{
if(px->index == py->index)
{
sum += px->value * py->value;
++px;
++py;
}
else
{
if(px->index > py->index)
++py;
else
++px;
}
}
return sum;
}
double Kernel::k_function(const svm_node *x, const svm_node *y,
const svm_parameter& param)
{
switch(param.kernel_type)
{
case LINEAR:
return dot(x,y);
case POLY:
return powi(param.gamma*dot(x,y)+param.coef0,param.degree);
case RBF:
{
double sum = 0;
while(x->index != -1 && y->index !=-1)
{
if(x->index == y->index)
{
double d = x->value - y->value;
sum += d*d;
++x;
++y;
}
else
{
if(x->index > y->index)
{
sum += y->value * y->value;
++y;
}
else
{
sum += x->value * x->value;
++x;
}
}
}
while(x->index != -1)
{
sum += x->value * x->value;
++x;
}
while(y->index != -1)
{
sum += y->value * y->value;
++y;
}
return exp(-param.gamma*sum);
}
case SIGMOID:
return tanh(param.gamma*dot(x,y)+param.coef0);
case PRECOMPUTED: //x: test (validation), y: SV
return x[(int)(y->value)].value;
default:
return 0; // Unreachable
}
}
// An SMO algorithm in Fan et al., JMLR 6(2005), p. 1889--1918
// Solves:
//
// min 0.5(\alpha^T Q \alpha) + p^T \alpha
//
// y^T \alpha = \delta
// y_i = +1 or -1
// 0 <= alpha_i <= Cp for y_i = 1
// 0 <= alpha_i <= Cn for y_i = -1
//
// Given:
//
// Q, p, y, Cp, Cn, and an initial feasible point \alpha
// l is the size of vectors and matrices
// eps is the stopping tolerance
//
// solution will be put in \alpha, objective value will be put in obj
//
class Solver {
public:
Solver() {};
virtual ~Solver() {};
struct SolutionInfo {
double obj;
double rho;
double *upper_bound;
double r; // for Solver_NU
};
void Solve(int l, const QMatrix& Q, const double *p_, const schar *y_,
double *alpha_, const double* C_, double eps,
SolutionInfo* si, int shrinking);
protected:
int active_size;
schar *y;
double *G; // gradient of objective function
enum { LOWER_BOUND, UPPER_BOUND, FREE };
char *alpha_status; // LOWER_BOUND, UPPER_BOUND, FREE
double *alpha;
const QMatrix *Q;
const double *QD;
double eps;
double Cp,Cn;
double *C;
double *p;
int *active_set;
double *G_bar; // gradient, if we treat free variables as 0
int l;
bool unshrink; // XXX
double get_C(int i)
{
return C[i];
}
void update_alpha_status(int i)
{
if(alpha[i] >= get_C(i))
alpha_status[i] = UPPER_BOUND;
else if(alpha[i] <= 0)
alpha_status[i] = LOWER_BOUND;
else alpha_status[i] = FREE;
}
bool is_upper_bound(int i) { return alpha_status[i] == UPPER_BOUND; }
bool is_lower_bound(int i) { return alpha_status[i] == LOWER_BOUND; }
bool is_free(int i) { return alpha_status[i] == FREE; }
void swap_index(int i, int j);
void reconstruct_gradient();
virtual int select_working_set(int &i, int &j);
virtual double calculate_rho();
virtual void do_shrinking();
private:
bool be_shrunk(int i, double Gmax1, double Gmax2);
};
void Solver::swap_index(int i, int j)
{
Q->swap_index(i,j);
swap(y[i],y[j]);
swap(G[i],G[j]);
swap(alpha_status[i],alpha_status[j]);
swap(alpha[i],alpha[j]);
swap(p[i],p[j]);
swap(active_set[i],active_set[j]);
swap(G_bar[i],G_bar[j]);
swap(C[i],C[j]);
}
void Solver::reconstruct_gradient()
{
// reconstruct inactive elements of G from G_bar and free variables
if(active_size == l) return;
int i,j;
int nr_free = 0;
for(j=active_size;j<l;j++)
G[j] = G_bar[j] + p[j];
for(j=0;j<active_size;j++)
if(is_free(j))
nr_free++;
if(2*nr_free < active_size)
info("\nWARNING: using -h 0 may be faster\n");
if (nr_free*l > 2*active_size*(l-active_size))
{
for(i=active_size;i<l;i++)
{
const Qfloat *Q_i = Q->get_Q(i,active_size);
for(j=0;j<active_size;j++)
if(is_free(j))
G[i] += alpha[j] * Q_i[j];
}
}
else
{
for(i=0;i<active_size;i++)
if(is_free(i))
{
const Qfloat *Q_i = Q->get_Q(i,l);
double alpha_i = alpha[i];
for(j=active_size;j<l;j++)
G[j] += alpha_i * Q_i[j];
}
}
}
void Solver::Solve(int l, const QMatrix& Q, const double *p_, const schar *y_,
double *alpha_, const double* C_, double eps,
SolutionInfo* si, int shrinking)
{
this->l = l;
this->Q = &Q;
QD=Q.get_QD();
clone(p, p_,l);
clone(y, y_,l);
clone(alpha,alpha_,l);
clone(C,C_,l);
this->eps = eps;
unshrink = false;
// initialize alpha_status
{
alpha_status = new char[l];
for(int i=0;i<l;i++)
update_alpha_status(i);
}
// initialize active set (for shrinking)
{
active_set = new int[l];
for(int i=0;i<l;i++)
active_set[i] = i;
active_size = l;
}
// initialize gradient
{
G = new double[l];
G_bar = new double[l];
int i;
for(i=0;i<l;i++)
{
G[i] = p[i];
G_bar[i] = 0;
}
for(i=0;i<l;i++)
if(!is_lower_bound(i))
{
const Qfloat *Q_i = Q.get_Q(i,l);
double alpha_i = alpha[i];
int j;
for(j=0;j<l;j++)
G[j] += alpha_i*Q_i[j];
if(is_upper_bound(i))
for(j=0;j<l;j++)
G_bar[j] += get_C(i) * Q_i[j];
}
}
// optimization step
int iter = 0;
int max_iter = max(10000000, l>INT_MAX/100 ? INT_MAX : 100*l);
int counter = min(l,1000)+1;
while(iter < max_iter)
{
// show progress and do shrinking
if(--counter == 0)
{
counter = min(l,1000);
if(shrinking) do_shrinking();
info(".");
}
int i,j;
if(select_working_set(i,j)!=0)
{
// reconstruct the whole gradient
reconstruct_gradient();
// reset active set size and check
active_size = l;
info("*");
if(select_working_set(i,j)!=0)
break;
else
counter = 1; // do shrinking next iteration
}
++iter;
// update alpha[i] and alpha[j], handle bounds carefully
const Qfloat *Q_i = Q.get_Q(i,active_size);
const Qfloat *Q_j = Q.get_Q(j,active_size);
double C_i = get_C(i);
double C_j = get_C(j);
double old_alpha_i = alpha[i];
double old_alpha_j = alpha[j];
if(y[i]!=y[j])
{
double quad_coef = QD[i]+QD[j]+2*Q_i[j];
if (quad_coef <= 0)
quad_coef = TAU;
double delta = (-G[i]-G[j])/quad_coef;
double diff = alpha[i] - alpha[j];
alpha[i] += delta;
alpha[j] += delta;
if(diff > 0)
{
if(alpha[j] < 0)
{
alpha[j] = 0;
alpha[i] = diff;
}
}
else
{
if(alpha[i] < 0)
{
alpha[i] = 0;
alpha[j] = -diff;
}
}
if(diff > C_i - C_j)
{
if(alpha[i] > C_i)
{
alpha[i] = C_i;
alpha[j] = C_i - diff;
}
}
else
{
if(alpha[j] > C_j)
{
alpha[j] = C_j;
alpha[i] = C_j + diff;
}
}
}
else
{
double quad_coef = QD[i]+QD[j]-2*Q_i[j];
if (quad_coef <= 0)
quad_coef = TAU;
double delta = (G[i]-G[j])/quad_coef;
double sum = alpha[i] + alpha[j];
alpha[i] -= delta;
alpha[j] += delta;
if(sum > C_i)
{
if(alpha[i] > C_i)
{
alpha[i] = C_i;
alpha[j] = sum - C_i;
}
}
else
{
if(alpha[j] < 0)
{
alpha[j] = 0;
alpha[i] = sum;
}
}
if(sum > C_j)
{
if(alpha[j] > C_j)
{
alpha[j] = C_j;
alpha[i] = sum - C_j;
}
}
else
{
if(alpha[i] < 0)
{
alpha[i] = 0;
alpha[j] = sum;
}
}
}
// update G
double delta_alpha_i = alpha[i] - old_alpha_i;
double delta_alpha_j = alpha[j] - old_alpha_j;
for(int k=0;k<active_size;k++)
{
G[k] += Q_i[k]*delta_alpha_i + Q_j[k]*delta_alpha_j;
}
// update alpha_status and G_bar
{
bool ui = is_upper_bound(i);
bool uj = is_upper_bound(j);
update_alpha_status(i);
update_alpha_status(j);
int k;
if(ui != is_upper_bound(i))
{
Q_i = Q.get_Q(i,l);
if(ui)
for(k=0;k<l;k++)
G_bar[k] -= C_i * Q_i[k];
else
for(k=0;k<l;k++)
G_bar[k] += C_i * Q_i[k];
}
if(uj != is_upper_bound(j))
{
Q_j = Q.get_Q(j,l);
if(uj)
for(k=0;k<l;k++)
G_bar[k] -= C_j * Q_j[k];
else
for(k=0;k<l;k++)
G_bar[k] += C_j * Q_j[k];
}
}
}
if(iter >= max_iter)
{
if(active_size < l)
{
// reconstruct the whole gradient to calculate objective value
reconstruct_gradient();
active_size = l;
info("*");
}
fprintf(stderr,"\nWARNING: reaching max number of iterations\n");
}
// calculate rho
si->rho = calculate_rho();
// calculate objective value
{
double v = 0;
int i;
for(i=0;i<l;i++)
v += alpha[i] * (G[i] + p[i]);
si->obj = v/2;
}
// put back the solution
{
for(int i=0;i<l;i++)
alpha_[active_set[i]] = alpha[i];
}
// juggle everything back
/*{
for(int i=0;i<l;i++)
while(active_set[i] != i)
swap_index(i,active_set[i]);
// or Q.swap_index(i,active_set[i]);
}*/
for(int i=0;i<l;i++)
si->upper_bound[i] = C[i];
info("\noptimization finished, #iter = %d\n",iter);
delete[] p;
delete[] y;
delete[] C;
delete[] alpha;
delete[] alpha_status;
delete[] active_set;
delete[] G;
delete[] G_bar;
}
// return 1 if already optimal, return 0 otherwise
int Solver::select_working_set(int &out_i, int &out_j)
{
// return i,j such that
// i: maximizes -y_i * grad(f)_i, i in I_up(\alpha)
// j: minimizes the decrease of obj value
// (if quadratic coefficeint <= 0, replace it with tau)
// -y_j*grad(f)_j < -y_i*grad(f)_i, j in I_low(\alpha)
double Gmax = -INF;
double Gmax2 = -INF;
int Gmax_idx = -1;
int Gmin_idx = -1;
double obj_diff_min = INF;
for(int t=0;t<active_size;t++)
if(y[t]==+1)
{
if(!is_upper_bound(t))
if(-G[t] >= Gmax)
{
Gmax = -G[t];
Gmax_idx = t;
}
}
else
{
if(!is_lower_bound(t))
if(G[t] >= Gmax)
{
Gmax = G[t];
Gmax_idx = t;
}
}
int i = Gmax_idx;
const Qfloat *Q_i = nullptr;
if(i != -1) // nullptr Q_i not accessed: Gmax=-INF if i=-1
Q_i = Q->get_Q(i,active_size);
for(int j=0;j<active_size;j++)
{
if(y[j]==+1)
{
if (!is_lower_bound(j))
{
double grad_diff=Gmax+G[j];
if (G[j] >= Gmax2)
Gmax2 = G[j];
if (grad_diff > 0)
{
double obj_diff;
double quad_coef = QD[i]+QD[j]-2.0*y[i]*Q_i[j];
if (quad_coef > 0)
obj_diff = -(grad_diff*grad_diff)/quad_coef;
else
obj_diff = -(grad_diff*grad_diff)/TAU;
if (obj_diff <= obj_diff_min)
{
Gmin_idx=j;
obj_diff_min = obj_diff;
}
}
}
}
else
{
if (!is_upper_bound(j))
{
double grad_diff= Gmax-G[j];
if (-G[j] >= Gmax2)
Gmax2 = -G[j];
if (grad_diff > 0)
{
double obj_diff;
double quad_coef = QD[i]+QD[j]+2.0*y[i]*Q_i[j];
if (quad_coef > 0)
obj_diff = -(grad_diff*grad_diff)/quad_coef;
else
obj_diff = -(grad_diff*grad_diff)/TAU;
if (obj_diff <= obj_diff_min)
{
Gmin_idx=j;
obj_diff_min = obj_diff;
}
}
}
}
}
if(Gmax+Gmax2 < eps)
return 1;
out_i = Gmax_idx;
out_j = Gmin_idx;
return 0;
}
bool Solver::be_shrunk(int i, double Gmax1, double Gmax2)
{
if(is_upper_bound(i))
{
if(y[i]==+1)
return(-G[i] > Gmax1);
else
return(-G[i] > Gmax2);
}
else if(is_lower_bound(i))
{
if(y[i]==+1)
return(G[i] > Gmax2);
else
return(G[i] > Gmax1);
}
else
return(false);
}
void Solver::do_shrinking()
{
int i;
double Gmax1 = -INF; // max { -y_i * grad(f)_i | i in I_up(\alpha) }
double Gmax2 = -INF; // max { y_i * grad(f)_i | i in I_low(\alpha) }
// find maximal violating pair first
for(i=0;i<active_size;i++)
{
if(y[i]==+1)
{
if(!is_upper_bound(i))
{
if(-G[i] >= Gmax1)
Gmax1 = -G[i];
}
if(!is_lower_bound(i))
{
if(G[i] >= Gmax2)
Gmax2 = G[i];
}
}
else
{
if(!is_upper_bound(i))
{
if(-G[i] >= Gmax2)
Gmax2 = -G[i];
}
if(!is_lower_bound(i))
{
if(G[i] >= Gmax1)
Gmax1 = G[i];
}
}
}
if(unshrink == false && Gmax1 + Gmax2 <= eps*10)
{
unshrink = true;
reconstruct_gradient();
active_size = l;
info("*");
}
for(i=0;i<active_size;i++)
if (be_shrunk(i, Gmax1, Gmax2))
{
active_size--;
while (active_size > i)
{
if (!be_shrunk(active_size, Gmax1, Gmax2))
{
swap_index(i,active_size);
break;
}
active_size--;
}
}
}
double Solver::calculate_rho()
{
double r;
int nr_free = 0;
double ub = INF, lb = -INF, sum_free = 0;
for(int i=0;i<active_size;i++)
{
double yG = y[i]*G[i];
if(is_upper_bound(i))
{
if(y[i]==-1)
ub = min(ub,yG);
else
lb = max(lb,yG);
}
else if(is_lower_bound(i))
{
if(y[i]==+1)
ub = min(ub,yG);
else
lb = max(lb,yG);
}
else
{
++nr_free;
sum_free += yG;
}
}
if(nr_free>0)
r = sum_free/nr_free;
else
r = (ub+lb)/2;
return r;
}
//
// Solver for nu-svm classification and regression
//
// additional constraint: e^T \alpha = constant
//
class Solver_NU: public Solver
{
public:
Solver_NU() {}
void Solve(int l, const QMatrix& Q, const double *p, const schar *y,
double *alpha, double* C_, double eps,
SolutionInfo* si, int shrinking)
{
this->si = si;
Solver::Solve(l,Q,p,y,alpha,C_,eps,si,shrinking);
}
private:
SolutionInfo *si;
int select_working_set(int &i, int &j) override;
double calculate_rho() override;
bool be_shrunk(int i, double Gmax1, double Gmax2, double Gmax3, double Gmax4);
void do_shrinking() override;
};
// return 1 if already optimal, return 0 otherwise
int Solver_NU::select_working_set(int &out_i, int &out_j)
{
// return i,j such that y_i = y_j and
// i: maximizes -y_i * grad(f)_i, i in I_up(\alpha)
// j: minimizes the decrease of obj value
// (if quadratic coefficeint <= 0, replace it with tau)
// -y_j*grad(f)_j < -y_i*grad(f)_i, j in I_low(\alpha)
double Gmaxp = -INF;
double Gmaxp2 = -INF;
int Gmaxp_idx = -1;
double Gmaxn = -INF;
double Gmaxn2 = -INF;
int Gmaxn_idx = -1;
int Gmin_idx = -1;
double obj_diff_min = INF;
for(int t=0;t<active_size;t++)
if(y[t]==+1)
{
if(!is_upper_bound(t))
if(-G[t] >= Gmaxp)
{
Gmaxp = -G[t];
Gmaxp_idx = t;
}
}
else
{
if(!is_lower_bound(t))
if(G[t] >= Gmaxn)
{
Gmaxn = G[t];
Gmaxn_idx = t;
}
}
int ip = Gmaxp_idx;
int in = Gmaxn_idx;
const Qfloat *Q_ip = nullptr;
const Qfloat *Q_in = nullptr;
if(ip != -1) // nullptr Q_ip not accessed: Gmaxp=-INF if ip=-1
Q_ip = Q->get_Q(ip,active_size);
if(in != -1)
Q_in = Q->get_Q(in,active_size);
for(int j=0;j<active_size;j++)
{
if(y[j]==+1)
{
if (!is_lower_bound(j))
{
double grad_diff=Gmaxp+G[j];
if (G[j] >= Gmaxp2)
Gmaxp2 = G[j];
if (grad_diff > 0)
{
double obj_diff;
double quad_coef = QD[ip]+QD[j]-2*Q_ip[j];
if (quad_coef > 0)
obj_diff = -(grad_diff*grad_diff)/quad_coef;
else
obj_diff = -(grad_diff*grad_diff)/TAU;
if (obj_diff <= obj_diff_min)
{
Gmin_idx=j;
obj_diff_min = obj_diff;
}
}
}
}
else
{
if (!is_upper_bound(j))
{
double grad_diff=Gmaxn-G[j];
if (-G[j] >= Gmaxn2)
Gmaxn2 = -G[j];
if (grad_diff > 0)
{
double obj_diff;
double quad_coef = QD[in]+QD[j]-2*Q_in[j];
if (quad_coef > 0)
obj_diff = -(grad_diff*grad_diff)/quad_coef;
else
obj_diff = -(grad_diff*grad_diff)/TAU;
if (obj_diff <= obj_diff_min)
{
Gmin_idx=j;
obj_diff_min = obj_diff;
}
}
}
}
}
if(max(Gmaxp+Gmaxp2,Gmaxn+Gmaxn2) < eps)
return 1;
if (y[Gmin_idx] == +1)
out_i = Gmaxp_idx;
else
out_i = Gmaxn_idx;
out_j = Gmin_idx;
return 0;
}
bool Solver_NU::be_shrunk(int i, double Gmax1, double Gmax2, double Gmax3, double Gmax4)
{
if(is_upper_bound(i))
{
if(y[i]==+1)
return(-G[i] > Gmax1);
else
return(-G[i] > Gmax4);
}
else if(is_lower_bound(i))
{
if(y[i]==+1)
return(G[i] > Gmax2);
else
return(G[i] > Gmax3);
}
else
return(false);
}
void Solver_NU::do_shrinking()
{
double Gmax1 = -INF; // max { -y_i * grad(f)_i | y_i = +1, i in I_up(\alpha) }
double Gmax2 = -INF; // max { y_i * grad(f)_i | y_i = +1, i in I_low(\alpha) }
double Gmax3 = -INF; // max { -y_i * grad(f)_i | y_i = -1, i in I_up(\alpha) }
double Gmax4 = -INF; // max { y_i * grad(f)_i | y_i = -1, i in I_low(\alpha) }
// find maximal violating pair first
int i;
for(i=0;i<active_size;i++)
{
if(!is_upper_bound(i))
{
if(y[i]==+1)
{
if(-G[i] > Gmax1) Gmax1 = -G[i];
}
else if(-G[i] > Gmax4) Gmax4 = -G[i];
}
if(!is_lower_bound(i))
{
if(y[i]==+1)
{
if(G[i] > Gmax2) Gmax2 = G[i];
}
else if(G[i] > Gmax3) Gmax3 = G[i];
}
}
if(unshrink == false && max(Gmax1+Gmax2,Gmax3+Gmax4) <= eps*10)
{
unshrink = true;
reconstruct_gradient();
active_size = l;
}
for(i=0;i<active_size;i++)
if (be_shrunk(i, Gmax1, Gmax2, Gmax3, Gmax4))
{
active_size--;
while (active_size > i)
{
if (!be_shrunk(active_size, Gmax1, Gmax2, Gmax3, Gmax4))
{
swap_index(i,active_size);
break;
}
active_size--;
}
}
}
double Solver_NU::calculate_rho()
{
int nr_free1 = 0,nr_free2 = 0;
double ub1 = INF, ub2 = INF;
double lb1 = -INF, lb2 = -INF;
double sum_free1 = 0, sum_free2 = 0;
for(int i=0;i<active_size;i++)
{
if(y[i]==+1)
{
if(is_upper_bound(i))
lb1 = max(lb1,G[i]);
else if(is_lower_bound(i))
ub1 = min(ub1,G[i]);
else
{
++nr_free1;
sum_free1 += G[i];
}
}
else
{
if(is_upper_bound(i))
lb2 = max(lb2,G[i]);
else if(is_lower_bound(i))
ub2 = min(ub2,G[i]);
else
{
++nr_free2;
sum_free2 += G[i];
}
}
}
double r1,r2;
if(nr_free1 > 0)
r1 = sum_free1/nr_free1;
else
r1 = (ub1+lb1)/2;
if(nr_free2 > 0)
r2 = sum_free2/nr_free2;
else
r2 = (ub2+lb2)/2;
si->r = (r1+r2)/2;
return (r1-r2)/2;
}
//
// Q matrices for various formulations
//
class SVC_Q: public Kernel
{
public:
SVC_Q(const svm_problem& prob, const svm_parameter& param, const schar *y_)
:Kernel(prob.l, prob.x, param)
{
clone(y,y_,prob.l);
cache = new Cache(prob.l,(long int)(param.cache_size*(1<<20)));
QD = new double[prob.l];
for(int i=0;i<prob.l;i++)
QD[i] = (this->*kernel_function)(i,i);
}
Qfloat *get_Q(int i, int len) const override
{
Qfloat *data;
int start, j;
if((start = cache->get_data(i,&data,len)) < len)
{
for(j=start;j<len;j++)
data[j] = (Qfloat)(y[i]*y[j]*(this->*kernel_function)(i,j));
}
return data;
}
double *get_QD() const override
{
return QD;
}
void swap_index(int i, int j) const override
{
cache->swap_index(i,j);
Kernel::swap_index(i,j);
swap(y[i],y[j]);
swap(QD[i],QD[j]);
}
~SVC_Q() override
{
delete[] y;
delete cache;
delete[] QD;
}
private:
schar *y;
Cache *cache;
double *QD;
};
class ONE_CLASS_Q: public Kernel
{
public:
ONE_CLASS_Q(const svm_problem& prob, const svm_parameter& param)
:Kernel(prob.l, prob.x, param)
{
cache = new Cache(prob.l,(long int)(param.cache_size*(1<<20)));
QD = new double[prob.l];
for(int i=0;i<prob.l;i++)
QD[i] = (this->*kernel_function)(i,i);
}
Qfloat *get_Q(int i, int len) const override
{
Qfloat *data;
int start, j;
if((start = cache->get_data(i,&data,len)) < len)
{
for(j=start;j<len;j++)
data[j] = (Qfloat)(this->*kernel_function)(i,j);
}
return data;
}
double *get_QD() const override
{
return QD;
}
void swap_index(int i, int j) const override
{
cache->swap_index(i,j);
Kernel::swap_index(i,j);
swap(QD[i],QD[j]);
}
~ONE_CLASS_Q() override
{
delete cache;
delete[] QD;
}
private:
Cache *cache;
double *QD;
};
class SVR_Q: public Kernel
{
public:
SVR_Q(const svm_problem& prob, const svm_parameter& param)
:Kernel(prob.l, prob.x, param)
{
l = prob.l;
cache = new Cache(l,(long int)(param.cache_size*(1<<20)));
QD = new double[2*l];
sign = new schar[2*l];
index = new int[2*l];
for(int k=0;k<l;k++)
{
sign[k] = 1;
sign[k+l] = -1;
index[k] = k;
index[k+l] = k;
QD[k] = (this->*kernel_function)(k,k);
QD[k+l] = QD[k];
}
buffer[0] = new Qfloat[2*l];
buffer[1] = new Qfloat[2*l];
next_buffer = 0;
}
void swap_index(int i, int j) const override
{
swap(sign[i],sign[j]);
swap(index[i],index[j]);
swap(QD[i],QD[j]);
}
Qfloat *get_Q(int i, int len) const override
{
Qfloat *data;
int j, real_i = index[i];
if(cache->get_data(real_i,&data,l) < l)
{
for(j=0;j<l;j++)
data[j] = (Qfloat)(this->*kernel_function)(real_i,j);
}
// reorder and copy
Qfloat *buf = buffer[next_buffer];
next_buffer = 1 - next_buffer;
schar si = sign[i];
for(j=0;j<len;j++)
buf[j] = (Qfloat) si * (Qfloat) sign[j] * data[index[j]];
return buf;
}
double *get_QD() const override
{
return QD;
}
~SVR_Q() override
{
delete cache;
delete[] sign;
delete[] index;
delete[] buffer[0];
delete[] buffer[1];
delete[] QD;
}
private:
int l;
Cache *cache;
schar *sign;
int *index;
mutable int next_buffer;
Qfloat *buffer[2];
double *QD;
};
//
// construct and solve various formulations
//
static void solve_c_svc(
const svm_problem *prob, const svm_parameter* param,
double *alpha, Solver::SolutionInfo* si, double Cp, double Cn)
{
int l = prob->l;
auto *minus_ones = new double[l];
auto *y = new schar[l];
auto *C = new double[l];
int i;
for(i=0;i<l;i++)
{
alpha[i] = 0;
minus_ones[i] = -1;
if(prob->y[i] > 0)
{
y[i] = +1;
C[i] = prob->W[i]*Cp;
}
else
{
y[i] = -1;
C[i] = prob->W[i]*Cn;
}
}
Solver s;
s.Solve(l, SVC_Q(*prob,*param,y), minus_ones, y,
alpha, C, param->eps, si, param->shrinking);
/*
double sum_alpha=0;
for(i=0;i<l;i++)
sum_alpha += alpha[i];
if (Cp==Cn)
info("nu = %f\n", sum_alpha/(Cp*prob->l));
*/
for(i=0;i<l;i++)
alpha[i] *= y[i];
delete[] C;
delete[] minus_ones;
delete[] y;
}
static void solve_nu_svc(
const svm_problem *prob, const svm_parameter *param,
double *alpha, Solver::SolutionInfo* si)
{
int i;
int l = prob->l;
double nu = param->nu;
auto *y = new schar[l];
auto *C = new double[l];
for(i=0;i<l;i++)
{
if(prob->y[i]>0)
y[i] = +1;
else
y[i] = -1;
C[i] = prob->W[i];
}
double nu_l = 0;
for(i=0;i<l;i++) nu_l += nu*C[i];
double sum_pos = nu_l/2;
double sum_neg = nu_l/2;
for(i=0;i<l;i++)
if(y[i] == +1)
{
alpha[i] = min(C[i],sum_pos);
sum_pos -= alpha[i];
}
else
{
alpha[i] = min(C[i],sum_neg);
sum_neg -= alpha[i];
}
auto *zeros = new double[l];
for(i=0;i<l;i++)
zeros[i] = 0;
Solver_NU s;
s.Solve(l, SVC_Q(*prob,*param,y), zeros, y,
alpha, C, param->eps, si, param->shrinking);
double r = si->r;
info("C = %f\n",1/r);
for(i=0;i<l;i++)
{
alpha[i] *= y[i]/r;
si->upper_bound[i] /= r;
}
si->rho /= r;
si->obj /= (r*r);
delete[] C;
delete[] y;
delete[] zeros;
}
static void solve_one_class(
const svm_problem *prob, const svm_parameter *param,
double *alpha, Solver::SolutionInfo* si)
{
int l = prob->l;
auto *zeros = new double[l];
auto *ones = new schar[l];
auto *C = new double[l];
int i;
double nu_l = 0;
for(i=0;i<l;i++)
{
C[i] = prob->W[i];
nu_l += C[i] * param->nu;
}
i = 0;
while(nu_l > 0)
{
alpha[i] = min(C[i],nu_l);
nu_l -= alpha[i];
++i;
}
for(;i<l;i++)
alpha[i] = 0;
for(i=0;i<l;i++)
{
zeros[i] = 0;
ones[i] = 1;
}
Solver s;
s.Solve(l, ONE_CLASS_Q(*prob,*param), zeros, ones,
alpha, C, param->eps, si, param->shrinking);
delete[] C;
delete[] zeros;
delete[] ones;
}
static void solve_epsilon_svr(
const svm_problem *prob, const svm_parameter *param,
double *alpha, Solver::SolutionInfo* si)
{
int l = prob->l;
auto *alpha2 = new double[2*l];
auto *linear_term = new double[2*l];
auto *C = new double[2*l];
auto *y = new schar[2*l];
int i;
for(i=0;i<l;i++)
{
alpha2[i] = 0;
linear_term[i] = param->p - prob->y[i];
y[i] = 1;
C[i] = prob->W[i]*param->C;
alpha2[i+l] = 0;
linear_term[i+l] = param->p + prob->y[i];
y[i+l] = -1;
C[i+l] = prob->W[i]*param->C;
}
Solver s;
s.Solve(2*l, SVR_Q(*prob,*param), linear_term, y,
alpha2, C, param->eps, si, param->shrinking);
- double sum_alpha = 0;
for(i=0;i<l;i++)
{
alpha[i] = alpha2[i] - alpha2[i+l];
- sum_alpha += fabs(alpha[i]);
}
//info("nu = %f\n",sum_alpha/(param->C*l));
delete[] alpha2;
delete[] linear_term;
delete[] C;
delete[] y;
}
static void solve_nu_svr(
const svm_problem *prob, const svm_parameter *param,
double *alpha, Solver::SolutionInfo* si)
{
int l = prob->l;
auto *C = new double[2*l];
auto *alpha2 = new double[2*l];
auto *linear_term = new double[2*l];
auto *y = new schar[2*l];
int i;
double sum = 0;
for(i=0;i<l;i++)
{
C[i] = C[i+l] = prob->W[i]*param->C;
sum += C[i] * param->nu;
}
sum /= 2;
for(i=0;i<l;i++)
{
alpha2[i] = alpha2[i+l] = min(sum,C[i]);
sum -= alpha2[i];
linear_term[i] = - prob->y[i];
y[i] = 1;
linear_term[i+l] = prob->y[i];
y[i+l] = -1;
}
Solver_NU s;
s.Solve(2*l, SVR_Q(*prob,*param), linear_term, y,
alpha2, C, param->eps, si, param->shrinking);
info("epsilon = %f\n",-si->r);
for(i=0;i<l;i++)
alpha[i] = alpha2[i] - alpha2[i+l];
delete[] alpha2;
delete[] linear_term;
delete[] C;
delete[] y;
}
//
// decision_function
//
struct decision_function
{
double *alpha;
double rho;
};
static decision_function svm_train_one(
const svm_problem *prob, const svm_parameter *param,
double Cp, double Cn)
{
auto *alpha = Malloc(double,prob->l);
Solver::SolutionInfo si;
switch(param->svm_type)
{
case C_SVC:
si.upper_bound = Malloc(double,prob->l);
solve_c_svc(prob,param,alpha,&si,Cp,Cn);
break;
case NU_SVC:
si.upper_bound = Malloc(double,prob->l);
solve_nu_svc(prob,param,alpha,&si);
break;
case ONE_CLASS:
si.upper_bound = Malloc(double,prob->l);
solve_one_class(prob,param,alpha,&si);
break;
case EPSILON_SVR:
si.upper_bound = Malloc(double,2*prob->l);
solve_epsilon_svr(prob,param,alpha,&si);
break;
case NU_SVR:
si.upper_bound = Malloc(double,2*prob->l);
solve_nu_svr(prob,param,alpha,&si);
break;
}
info("obj = %f, rho = %f\n",si.obj,si.rho);
// output SVs
int nSV = 0;
int nBSV = 0;
for(int i=0;i<prob->l;i++)
{
if(fabs(alpha[i]) > 0)
{
++nSV;
if(prob->y[i] > 0)
{
if(fabs(alpha[i]) >= si.upper_bound[i])
++nBSV;
}
else
{
if(fabs(alpha[i]) >= si.upper_bound[i])
++nBSV;
}
}
}
free(si.upper_bound);
info("nSV = %d, nBSV = %d\n",nSV,nBSV);
decision_function f;
f.alpha = alpha;
f.rho = si.rho;
return f;
}
// Platt's binary SVM Probablistic Output: an improvement from Lin et al.
static void sigmoid_train(
int l, const double *dec_values, const double *labels,
double& A, double& B)
{
double prior1=0, prior0 = 0;
int i;
for (i=0;i<l;i++)
if (labels[i] > 0) prior1+=1;
else prior0+=1;
int max_iter=100; // Maximal number of iterations
double min_step=1e-10; // Minimal step taken in line search
double sigma=1e-12; // For numerically strict PD of Hessian
double eps=1e-5;
double hiTarget=(prior1+1.0)/(prior1+2.0);
double loTarget=1/(prior0+2.0);
auto *t=Malloc(double,l);
double fApB,p,q,h11,h22,h21,g1,g2,det,dA,dB,gd,stepsize;
double newA,newB,newf,d1,d2;
int iter;
// Initial Point and Initial Fun Value
A=0.0; B=log((prior0+1.0)/(prior1+1.0));
double fval = 0.0;
for (i=0;i<l;i++)
{
if (labels[i]>0) t[i]=hiTarget;
else t[i]=loTarget;
fApB = dec_values[i]*A+B;
if (fApB>=0)
fval += t[i]*fApB + log(1+exp(-fApB));
else
fval += (t[i] - 1)*fApB +log(1+exp(fApB));
}
for (iter=0;iter<max_iter;iter++)
{
// Update Gradient and Hessian (use H' = H + sigma I)
h11=sigma; // numerically ensures strict PD
h22=sigma;
h21=0.0;g1=0.0;g2=0.0;
for (i=0;i<l;i++)
{
fApB = dec_values[i]*A+B;
if (fApB >= 0)
{
p=exp(-fApB)/(1.0+exp(-fApB));
q=1.0/(1.0+exp(-fApB));
}
else
{
p=1.0/(1.0+exp(fApB));
q=exp(fApB)/(1.0+exp(fApB));
}
d2=p*q;
h11+=dec_values[i]*dec_values[i]*d2;
h22+=d2;
h21+=dec_values[i]*d2;
d1=t[i]-p;
g1+=dec_values[i]*d1;
g2+=d1;
}
// Stopping Criteria
if (fabs(g1)<eps && fabs(g2)<eps)
break;
// Finding Newton direction: -inv(H') * g
det=h11*h22-h21*h21;
dA=-(h22*g1 - h21 * g2) / det;
dB=-(-h21*g1+ h11 * g2) / det;
gd=g1*dA+g2*dB;
stepsize = 1; // Line Search
while (stepsize >= min_step)
{
newA = A + stepsize * dA;
newB = B + stepsize * dB;
// New function value
newf = 0.0;
for (i=0;i<l;i++)
{
fApB = dec_values[i]*newA+newB;
if (fApB >= 0)
newf += t[i]*fApB + log(1+exp(-fApB));
else
newf += (t[i] - 1)*fApB +log(1+exp(fApB));
}
// Check sufficient decrease
if (newf<fval+0.0001*stepsize*gd)
{
A=newA;B=newB;fval=newf;
break;
}
else
stepsize = stepsize / 2.0;
}
if (stepsize < min_step)
{
info("Line search fails in two-class probability estimates\n");
break;
}
}
if (iter>=max_iter)
info("Reaching maximal iterations in two-class probability estimates\n");
free(t);
}
static double sigmoid_predict(double decision_value, double A, double B)
{
double fApB = decision_value*A+B;
// 1-p used later; avoid catastrophic cancellation
if (fApB >= 0)
return exp(-fApB)/(1.0+exp(-fApB));
else
return 1.0/(1+exp(fApB)) ;
}
// Method 2 from the multiclass_prob paper by Wu, Lin, and Weng
static void multiclass_probability(int k, double **r, double *p)
{
int t,j;
int iter = 0, max_iter=max(100,k);
auto **Q=Malloc(double *,k);
auto *Qp=Malloc(double,k);
double pQp, eps=0.005/k;
for (t=0;t<k;t++)
{
p[t]=1.0/k; // Valid if k = 1
Q[t]=Malloc(double,k);
Q[t][t]=0;
for (j=0;j<t;j++)
{
Q[t][t]+=r[j][t]*r[j][t];
Q[t][j]=Q[j][t];
}
for (j=t+1;j<k;j++)
{
Q[t][t]+=r[j][t]*r[j][t];
Q[t][j]=-r[j][t]*r[t][j];
}
}
for (iter=0;iter<max_iter;iter++)
{
// stopping condition, recalculate QP,pQP for numerical accuracy
pQp=0;
for (t=0;t<k;t++)
{
Qp[t]=0;
for (j=0;j<k;j++)
Qp[t]+=Q[t][j]*p[j];
pQp+=p[t]*Qp[t];
}
double max_error=0;
for (t=0;t<k;t++)
{
double error=fabs(Qp[t]-pQp);
if (error>max_error)
max_error=error;
}
if (max_error<eps) break;
for (t=0;t<k;t++)
{
double diff=(-Qp[t]+pQp)/Q[t][t];
p[t]+=diff;
pQp=(pQp+diff*(diff*Q[t][t]+2*Qp[t]))/(1+diff)/(1+diff);
for (j=0;j<k;j++)
{
Qp[j]=(Qp[j]+diff*Q[t][j])/(1+diff);
p[j]/=(1+diff);
}
}
}
if (iter>=max_iter)
info("Exceeds max_iter in multiclass_prob\n");
for(t=0;t<k;t++) free(Q[t]);
free(Q);
free(Qp);
}
// Cross-validation decision values for probability estimates
static void svm_binary_svc_probability(
const svm_problem *prob, const svm_parameter *param,
double Cp, double Cn, double& probA, double& probB)
{
int i;
int nr_fold = 5;
auto *perm = Malloc(int,prob->l);
auto *dec_values = Malloc(double,prob->l);
// random shuffle
for(i=0;i<prob->l;i++) perm[i]=i;
for(i=0;i<prob->l;i++)
{
int j = i+rand()%(prob->l-i);
swap(perm[i],perm[j]);
}
for(i=0;i<nr_fold;i++)
{
int begin = i*prob->l/nr_fold;
int end = (i+1)*prob->l/nr_fold;
int j,k;
struct svm_problem subprob;
subprob.l = prob->l-(end-begin);
subprob.x = Malloc(struct svm_node*,subprob.l);
subprob.y = Malloc(double,subprob.l);
subprob.W = Malloc(double,subprob.l);
k=0;
for(j=0;j<begin;j++)
{
subprob.x[k] = prob->x[perm[j]];
subprob.y[k] = prob->y[perm[j]];
subprob.W[k] = prob->W[perm[j]];
++k;
}
for(j=end;j<prob->l;j++)
{
subprob.x[k] = prob->x[perm[j]];
subprob.y[k] = prob->y[perm[j]];
subprob.W[k] = prob->W[perm[j]];
++k;
}
int p_count=0,n_count=0;
for(j=0;j<k;j++)
if(subprob.y[j]>0)
p_count++;
else
n_count++;
if(p_count==0 && n_count==0)
for(j=begin;j<end;j++)
dec_values[perm[j]] = 0;
else if(p_count > 0 && n_count == 0)
for(j=begin;j<end;j++)
dec_values[perm[j]] = 1;
else if(p_count == 0 && n_count > 0)
for(j=begin;j<end;j++)
dec_values[perm[j]] = -1;
else
{
svm_parameter subparam = *param;
subparam.probability=0;
subparam.C=1.0;
subparam.nr_weight=2;
subparam.weight_label = Malloc(int,2);
subparam.weight = Malloc(double,2);
subparam.weight_label[0]=+1;
subparam.weight_label[1]=-1;
subparam.weight[0]=Cp;
subparam.weight[1]=Cn;
struct svm_model *submodel = svm_train(&subprob,&subparam);
for(j=begin;j<end;j++)
{
svm_predict_values(submodel,prob->x[perm[j]],&(dec_values[perm[j]]));
// ensure +1 -1 order; reason not using CV subroutine
dec_values[perm[j]] *= submodel->label[0];
}
svm_free_and_destroy_model(&submodel);
svm_destroy_param(&subparam);
}
free(subprob.x);
free(subprob.y);
free(subprob.W);
}
sigmoid_train(prob->l,dec_values,prob->y,probA,probB);
free(dec_values);
free(perm);
}
// Return parameter of a Laplace distribution
static double svm_svr_probability(
const svm_problem *prob, const svm_parameter *param)
{
int i;
int nr_fold = 5;
auto *ymv = Malloc(double,prob->l);
double mae = 0;
svm_parameter newparam = *param;
newparam.probability = 0;
svm_cross_validation(prob,&newparam,nr_fold,ymv);
for(i=0;i<prob->l;i++)
{
ymv[i]=prob->y[i]-ymv[i];
mae += fabs(ymv[i]);
}
mae /= prob->l;
double std=sqrt(2*mae*mae);
int count=0;
mae=0;
for(i=0;i<prob->l;i++)
if (fabs(ymv[i]) > 5*std)
count=count+1;
else
mae+=fabs(ymv[i]);
mae /= (prob->l-count);
info("Prob. model for test data: target value = predicted value + z,\nz: Laplace distribution e^(-|z|/sigma)/(2sigma),sigma= %g\n",mae);
free(ymv);
return mae;
}
// label: label name, start: begin of each class, count: #data of classes, perm: indices to the original data
// perm, length l, must be allocated before calling this subroutine
static void svm_group_classes(const svm_problem *prob, int *nr_class_ret, int **label_ret, int **start_ret, int **count_ret, int *perm)
{
int l = prob->l;
int max_nr_class = 16;
int nr_class = 0;
auto *label = Malloc(int,max_nr_class);
auto *count = Malloc(int,max_nr_class);
auto *data_label = Malloc(int,l);
int i;
for(i=0;i<l;i++)
{
auto this_label = (int)prob->y[i];
int j;
for(j=0;j<nr_class;j++)
{
if(this_label == label[j])
{
++count[j];
break;
}
}
data_label[i] = j;
if(j == nr_class)
{
if(nr_class == max_nr_class)
{
max_nr_class *= 2;
label = (int *)realloc(label,max_nr_class*sizeof(int));
count = (int *)realloc(count,max_nr_class*sizeof(int));
}
label[nr_class] = this_label;
count[nr_class] = 1;
++nr_class;
}
}
//
// Labels are ordered by their first occurrence in the training set.
// However, for two-class sets with -1/+1 labels and -1 appears first,
// we swap labels to ensure that internally the binary SVM has positive data corresponding to the +1 instances.
//
if (nr_class == 2 && label[0] == -1 && label[1] == 1)
{
swap(label[0],label[1]);
swap(count[0],count[1]);
for(i=0;i<l;i++)
{
if(data_label[i] == 0)
data_label[i] = 1;
else
data_label[i] = 0;
}
}
auto *start = Malloc(int,nr_class);
start[0] = 0;
for(i=1;i<nr_class;i++)
start[i] = start[i-1]+count[i-1];
for(i=0;i<l;i++)
{
perm[start[data_label[i]]] = i;
++start[data_label[i]];
}
start[0] = 0;
for(i=1;i<nr_class;i++)
start[i] = start[i-1]+count[i-1];
*nr_class_ret = nr_class;
*label_ret = label;
*start_ret = start;
*count_ret = count;
free(data_label);
}
//
// Remove zero weighed data as libsvm and some liblinear solvers require C > 0.
//
static void remove_zero_weight(svm_problem *newprob, const svm_problem *prob)
{
int i;
int l = 0;
for(i=0;i<prob->l;i++)
if(prob->W[i] > 0) l++;
*newprob = *prob;
newprob->l = l;
newprob->x = Malloc(svm_node*,l);
newprob->y = Malloc(double,l);
newprob->W = Malloc(double,l);
int j = 0;
for(i=0;i<prob->l;i++)
if(prob->W[i] > 0)
{
newprob->x[j] = prob->x[i];
newprob->y[j] = prob->y[i];
newprob->W[j] = prob->W[i];
j++;
}
}
//
// Interface functions
//
svm_model *svm_train(const svm_problem *prob, const svm_parameter *param)
{
svm_problem newprob;
remove_zero_weight(&newprob, prob);
prob = &newprob;
auto *model = Malloc(svm_model,1);
model->param = *param;
model->free_sv = 0; // XXX
if(param->svm_type == ONE_CLASS ||
param->svm_type == EPSILON_SVR ||
param->svm_type == NU_SVR)
{
// regression or one-class-svm
model->nr_class = 2;
model->label = nullptr;
model->nSV = nullptr;
model->probA = nullptr; model->probB = nullptr;
model->sv_coef = Malloc(double *,1);
if(param->probability &&
(param->svm_type == EPSILON_SVR ||
param->svm_type == NU_SVR))
{
model->probA = Malloc(double,1);
model->probA[0] = svm_svr_probability(prob,param);
}
decision_function f = svm_train_one(prob,param,0,0);
model->rho = Malloc(double,1);
model->rho[0] = f.rho;
int nSV = 0;
int i;
for(i=0;i<prob->l;i++)
if(fabs(f.alpha[i]) > 0) ++nSV;
model->l = nSV;
model->SV = Malloc(svm_node *,nSV);
model->sv_coef[0] = Malloc(double,nSV);
model->sv_indices = Malloc(int,nSV);
int j = 0;
for(i=0;i<prob->l;i++)
if(fabs(f.alpha[i]) > 0)
{
model->SV[j] = prob->x[i];
model->sv_coef[0][j] = f.alpha[i];
model->sv_indices[j] = i+1;
++j;
}
free(f.alpha);
}
else
{
// classification
int l = prob->l;
int nr_class;
int *label = nullptr;
int *start = nullptr;
int *count = nullptr;
auto *perm = Malloc(int,l);
// group training data of the same class
svm_group_classes(prob,&nr_class,&label,&start,&count,perm);
if(nr_class == 1)
info("WARNING: training data in only one class. See README for details.\n");
auto **x = Malloc(svm_node *,l);
double *W;
W = Malloc(double,l);
int i;
for(i=0;i<l;i++)
{
x[i] = prob->x[perm[i]];
W[i] = prob->W[perm[i]];
}
// calculate weighted C
auto *weighted_C = Malloc(double, nr_class);
for(i=0;i<nr_class;i++)
weighted_C[i] = param->C;
for(i=0;i<param->nr_weight;i++)
{
int j;
for(j=0;j<nr_class;j++)
if(param->weight_label[i] == label[j])
break;
if(j == nr_class)
fprintf(stderr,"WARNING: class label %d specified in weight is not found\n", param->weight_label[i]);
else
weighted_C[j] *= param->weight[i];
}
// train k*(k-1)/2 models
auto *nonzero = Malloc(bool,l);
for(i=0;i<l;i++)
nonzero[i] = false;
auto *f = Malloc(decision_function,nr_class*(nr_class-1)/2);
double *probA=nullptr,*probB=nullptr;
if (param->probability)
{
probA=Malloc(double,nr_class*(nr_class-1)/2);
probB=Malloc(double,nr_class*(nr_class-1)/2);
}
int p = 0;
for(i=0;i<nr_class;i++)
for(int j=i+1;j<nr_class;j++)
{
svm_problem sub_prob;
int si = start[i], sj = start[j];
int ci = count[i], cj = count[j];
sub_prob.l = ci+cj;
sub_prob.x = Malloc(svm_node *,sub_prob.l);
sub_prob.y = Malloc(double,sub_prob.l);
sub_prob.W = Malloc(double,sub_prob.l);
int k;
for(k=0;k<ci;k++)
{
sub_prob.x[k] = x[si+k];
sub_prob.y[k] = +1;
sub_prob.W[k] = W[si+k];
}
for(k=0;k<cj;k++)
{
sub_prob.x[ci+k] = x[sj+k];
sub_prob.y[ci+k] = -1;
sub_prob.W[ci+k] = W[sj+k];
}
if(param->probability)
svm_binary_svc_probability(&sub_prob,param,weighted_C[i],weighted_C[j],probA[p],probB[p]);
f[p] = svm_train_one(&sub_prob,param,weighted_C[i],weighted_C[j]);
for(k=0;k<ci;k++)
if(!nonzero[si+k] && fabs(f[p].alpha[k]) > 0)
nonzero[si+k] = true;
for(k=0;k<cj;k++)
if(!nonzero[sj+k] && fabs(f[p].alpha[ci+k]) > 0)
nonzero[sj+k] = true;
free(sub_prob.x);
free(sub_prob.y);
free(sub_prob.W);
++p;
}
// build output
model->nr_class = nr_class;
model->label = Malloc(int,nr_class);
for(i=0;i<nr_class;i++)
model->label[i] = label[i];
model->rho = Malloc(double,nr_class*(nr_class-1)/2);
for(i=0;i<nr_class*(nr_class-1)/2;i++)
model->rho[i] = f[i].rho;
if(param->probability)
{
model->probA = Malloc(double,nr_class*(nr_class-1)/2);
model->probB = Malloc(double,nr_class*(nr_class-1)/2);
for(i=0;i<nr_class*(nr_class-1)/2;i++)
{
model->probA[i] = probA[i];
model->probB[i] = probB[i];
}
}
else
{
model->probA=nullptr;
model->probB=nullptr;
}
int total_sv = 0;
auto *nz_count = Malloc(int,nr_class);
model->nSV = Malloc(int,nr_class);
for(i=0;i<nr_class;i++)
{
int nSV = 0;
for(int j=0;j<count[i];j++)
if(nonzero[start[i]+j])
{
++nSV;
++total_sv;
}
model->nSV[i] = nSV;
nz_count[i] = nSV;
}
info("Total nSV = %d\n",total_sv);
model->l = total_sv;
model->SV = Malloc(svm_node *,total_sv);
model->sv_indices = Malloc(int,total_sv);
p = 0;
for(i=0;i<l;i++)
if(nonzero[i])
{
model->SV[p] = x[i];
model->sv_indices[p++] = perm[i] + 1;
}
auto *nz_start = Malloc(int,nr_class);
nz_start[0] = 0;
for(i=1;i<nr_class;i++)
nz_start[i] = nz_start[i-1]+nz_count[i-1];
model->sv_coef = Malloc(double *,nr_class-1);
for(i=0;i<nr_class-1;i++)
model->sv_coef[i] = Malloc(double,total_sv);
p = 0;
for(i=0;i<nr_class;i++)
for(int j=i+1;j<nr_class;j++)
{
// classifier (i,j): coefficients with
// i are in sv_coef[j-1][nz_start[i]...],
// j are in sv_coef[i][nz_start[j]...]
int si = start[i];
int sj = start[j];
int ci = count[i];
int cj = count[j];
int q = nz_start[i];
int k;
for(k=0;k<ci;k++)
if(nonzero[si+k])
model->sv_coef[j-1][q++] = f[p].alpha[k];
q = nz_start[j];
for(k=0;k<cj;k++)
if(nonzero[sj+k])
model->sv_coef[i][q++] = f[p].alpha[ci+k];
++p;
}
free(label);
free(probA);
free(probB);
free(count);
free(perm);
free(start);
free(W);
free(x);
free(weighted_C);
free(nonzero);
for(i=0;i<nr_class*(nr_class-1)/2;i++)
free(f[i].alpha);
free(f);
free(nz_count);
free(nz_start);
}
free(newprob.x);
free(newprob.y);
free(newprob.W);
return model;
}
// Stratified cross validation
void svm_cross_validation(const svm_problem *prob, const svm_parameter *param, int nr_fold, double *target)
{
int i;
int *fold_start;
int l = prob->l;
auto *perm = Malloc(int,l);
int nr_class;
if (nr_fold > l)
{
nr_fold = l;
fprintf(stderr,"WARNING: # folds > # data. Will use # folds = # data instead (i.e., leave-one-out cross validation)\n");
}
fold_start = Malloc(int,nr_fold+1);
// stratified cv may not give leave-one-out rate
// Each class to l folds -> some folds may have zero elements
if((param->svm_type == C_SVC ||
param->svm_type == NU_SVC) && nr_fold < l)
{
int *start = nullptr;
int *label = nullptr;
int *count = nullptr;
svm_group_classes(prob,&nr_class,&label,&start,&count,perm);
// random shuffle and then data grouped by fold using the array perm
auto *fold_count = Malloc(int,nr_fold);
int c;
auto *index = Malloc(int,l);
for(i=0;i<l;i++)
index[i]=perm[i];
for (c=0; c<nr_class; c++)
for(i=0;i<count[c];i++)
{
int j = i+rand()%(count[c]-i);
swap(index[start[c]+j],index[start[c]+i]);
}
for(i=0;i<nr_fold;i++)
{
fold_count[i] = 0;
for (c=0; c<nr_class;c++)
fold_count[i]+=(i+1)*count[c]/nr_fold-i*count[c]/nr_fold;
}
fold_start[0]=0;
for (i=1;i<=nr_fold;i++)
fold_start[i] = fold_start[i-1]+fold_count[i-1];
for (c=0; c<nr_class;c++)
for(i=0;i<nr_fold;i++)
{
int begin = start[c]+i*count[c]/nr_fold;
int end = start[c]+(i+1)*count[c]/nr_fold;
for(int j=begin;j<end;j++)
{
perm[fold_start[i]] = index[j];
fold_start[i]++;
}
}
fold_start[0]=0;
for (i=1;i<=nr_fold;i++)
fold_start[i] = fold_start[i-1]+fold_count[i-1];
free(start);
free(label);
free(count);
free(index);
free(fold_count);
}
else
{
for(i=0;i<l;i++) perm[i]=i;
for(i=0;i<l;i++)
{
int j = i+rand()%(l-i);
swap(perm[i],perm[j]);
}
for(i=0;i<=nr_fold;i++)
fold_start[i]=i*l/nr_fold;
}
for(i=0;i<nr_fold;i++)
{
int begin = fold_start[i];
int end = fold_start[i+1];
int j,k;
struct svm_problem subprob;
subprob.l = l-(end-begin);
subprob.x = Malloc(struct svm_node*,subprob.l);
subprob.y = Malloc(double,subprob.l);
subprob.W = Malloc(double,subprob.l);
k=0;
for(j=0;j<begin;j++)
{
subprob.x[k] = prob->x[perm[j]];
subprob.y[k] = prob->y[perm[j]];
subprob.W[k] = prob->W[perm[j]];
++k;
}
for(j=end;j<l;j++)
{
subprob.x[k] = prob->x[perm[j]];
subprob.y[k] = prob->y[perm[j]];
subprob.W[k] = prob->W[perm[j]];
++k;
}
struct svm_model *submodel = svm_train(&subprob,param);
if(param->probability &&
(param->svm_type == C_SVC || param->svm_type == NU_SVC))
{
auto *prob_estimates=Malloc(double,svm_get_nr_class(submodel));
for(j=begin;j<end;j++)
target[perm[j]] = svm_predict_probability(submodel,prob->x[perm[j]],prob_estimates);
free(prob_estimates);
}
else
for(j=begin;j<end;j++)
target[perm[j]] = svm_predict(submodel,prob->x[perm[j]]);
svm_free_and_destroy_model(&submodel);
free(subprob.x);
free(subprob.y);
free(subprob.W);
}
free(fold_start);
free(perm);
}
int svm_get_svm_type(const svm_model *model)
{
return model->param.svm_type;
}
int svm_get_nr_class(const svm_model *model)
{
return model->nr_class;
}
void svm_get_labels(const svm_model *model, int* label)
{
if (model->label != nullptr)
for(int i=0;i<model->nr_class;i++)
label[i] = model->label[i];
}
void svm_get_sv_indices(const svm_model *model, int* indices)
{
if (model->sv_indices != nullptr)
for(int i=0;i<model->l;i++)
indices[i] = model->sv_indices[i];
}
int svm_get_nr_sv(const svm_model *model)
{
return model->l;
}
double svm_get_svr_probability(const svm_model *model)
{
if ((model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) &&
model->probA!=nullptr)
return model->probA[0];
else
{
fprintf(stderr,"Model doesn't contain information for SVR probability inference\n");
return 0;
}
}
double svm_predict_values(const svm_model *model, const svm_node *x, double* dec_values)
{
int i;
if(model->param.svm_type == ONE_CLASS ||
model->param.svm_type == EPSILON_SVR ||
model->param.svm_type == NU_SVR)
{
double *sv_coef = model->sv_coef[0];
double sum = 0;
for(i=0;i<model->l;i++)
sum += sv_coef[i] * Kernel::k_function(x,model->SV[i],model->param);
sum -= model->rho[0];
*dec_values = sum;
if(model->param.svm_type == ONE_CLASS)
return (sum>0)?1:-1;
else
return sum;
}
else
{
int nr_class = model->nr_class;
int l = model->l;
auto *kvalue = Malloc(double,l);
for(i=0;i<l;i++)
kvalue[i] = Kernel::k_function(x,model->SV[i],model->param);
auto *start = Malloc(int,nr_class);
start[0] = 0;
for(i=1;i<nr_class;i++)
start[i] = start[i-1]+model->nSV[i-1];
auto *vote = Malloc(int,nr_class);
for(i=0;i<nr_class;i++)
vote[i] = 0;
int p=0;
for(i=0;i<nr_class;i++)
for(int j=i+1;j<nr_class;j++)
{
double sum = 0;
int si = start[i];
int sj = start[j];
int ci = model->nSV[i];
int cj = model->nSV[j];
int k;
double *coef1 = model->sv_coef[j-1];
double *coef2 = model->sv_coef[i];
for(k=0;k<ci;k++)
sum += coef1[si+k] * kvalue[si+k];
for(k=0;k<cj;k++)
sum += coef2[sj+k] * kvalue[sj+k];
sum -= model->rho[p];
dec_values[p] = sum;
if(dec_values[p] > 0)
++vote[i];
else
++vote[j];
p++;
}
int vote_max_idx = 0;
for(i=1;i<nr_class;i++)
if(vote[i] > vote[vote_max_idx])
vote_max_idx = i;
free(kvalue);
free(start);
free(vote);
return model->label[vote_max_idx];
}
}
double svm_predict(const svm_model *model, const svm_node *x)
{
int nr_class = model->nr_class;
double *dec_values;
if(model->param.svm_type == ONE_CLASS ||
model->param.svm_type == EPSILON_SVR ||
model->param.svm_type == NU_SVR)
dec_values = Malloc(double, 1);
else
dec_values = Malloc(double, nr_class*(nr_class-1)/2);
double pred_result = svm_predict_values(model, x, dec_values);
free(dec_values);
return pred_result;
}
double svm_predict_probability(
const svm_model *model, const svm_node *x, double *prob_estimates)
{
if ((model->param.svm_type == C_SVC || model->param.svm_type == NU_SVC) &&
model->probA!=nullptr && model->probB!=nullptr)
{
int i;
int nr_class = model->nr_class;
auto *dec_values = Malloc(double, nr_class*(nr_class-1)/2);
svm_predict_values(model, x, dec_values);
double min_prob=1e-7;
auto **pairwise_prob=Malloc(double *,nr_class);
for(i=0;i<nr_class;i++)
pairwise_prob[i]=Malloc(double,nr_class);
int k=0;
for(i=0;i<nr_class;i++)
for(int j=i+1;j<nr_class;j++)
{
pairwise_prob[i][j]=min(max(sigmoid_predict(dec_values[k],model->probA[k],model->probB[k]),min_prob),1-min_prob);
pairwise_prob[j][i]=1-pairwise_prob[i][j];
k++;
}
multiclass_probability(nr_class,pairwise_prob,prob_estimates);
int prob_max_idx = 0;
for(i=1;i<nr_class;i++)
if(prob_estimates[i] > prob_estimates[prob_max_idx])
prob_max_idx = i;
for(i=0;i<nr_class;i++)
free(pairwise_prob[i]);
free(dec_values);
free(pairwise_prob);
return model->label[prob_max_idx];
}
else
return svm_predict(model, x);
}
static const char *svm_type_table[] =
{
"c_svc","nu_svc","one_class","epsilon_svr","nu_svr",nullptr
};
static const char *kernel_type_table[]=
{
"linear","polynomial","rbf","sigmoid","precomputed",nullptr
};
int svm_save_model(const char *model_file_name, const svm_model *model)
{
FILE *fp = fopen(model_file_name,"w");
if(fp==nullptr) return -1;
mitk::LocaleSwitch localeSwitch("C");
const svm_parameter& param = model->param;
fprintf(fp,"svm_type %s\n", svm_type_table[param.svm_type]);
fprintf(fp,"kernel_type %s\n", kernel_type_table[param.kernel_type]);
if(param.kernel_type == POLY)
fprintf(fp,"degree %d\n", param.degree);
if(param.kernel_type == POLY || param.kernel_type == RBF || param.kernel_type == SIGMOID)
fprintf(fp,"gamma %g\n", param.gamma);
if(param.kernel_type == POLY || param.kernel_type == SIGMOID)
fprintf(fp,"coef0 %g\n", param.coef0);
int nr_class = model->nr_class;
int l = model->l;
fprintf(fp, "nr_class %d\n", nr_class);
fprintf(fp, "total_sv %d\n",l);
{
fprintf(fp, "rho");
for(int i=0;i<nr_class*(nr_class-1)/2;i++)
fprintf(fp," %g",model->rho[i]);
fprintf(fp, "\n");
}
if(model->label)
{
fprintf(fp, "label");
for(int i=0;i<nr_class;i++)
fprintf(fp," %d",model->label[i]);
fprintf(fp, "\n");
}
if(model->probA) // regression has probA only
{
fprintf(fp, "probA");
for(int i=0;i<nr_class*(nr_class-1)/2;i++)
fprintf(fp," %g",model->probA[i]);
fprintf(fp, "\n");
}
if(model->probB)
{
fprintf(fp, "probB");
for(int i=0;i<nr_class*(nr_class-1)/2;i++)
fprintf(fp," %g",model->probB[i]);
fprintf(fp, "\n");
}
if(model->nSV)
{
fprintf(fp, "nr_sv");
for(int i=0;i<nr_class;i++)
fprintf(fp," %d",model->nSV[i]);
fprintf(fp, "\n");
}
fprintf(fp, "SV\n");
const double * const *sv_coef = model->sv_coef;
const svm_node * const *SV = model->SV;
for(int i=0;i<l;i++)
{
for(int j=0;j<nr_class-1;j++)
fprintf(fp, "%.16g ",sv_coef[j][i]);
const svm_node *p = SV[i];
if(param.kernel_type == PRECOMPUTED)
fprintf(fp,"0:%d ",(int)(p->value));
else
while(p->index != -1)
{
fprintf(fp,"%d:%.8g ",p->index,p->value);
p++;
}
fprintf(fp, "\n");
}
if (ferror(fp) != 0 || fclose(fp) != 0) return -1;
else return 0;
}
static char *line = nullptr;
static int max_line_len;
static char* readline(FILE *input)
{
int len;
if(fgets(line,max_line_len,input) == nullptr)
return nullptr;
while(strrchr(line,'\n') == nullptr)
{
max_line_len *= 2;
line = (char *) realloc(line,max_line_len);
len = (int) strlen(line);
if(fgets(line+len,max_line_len-len,input) == nullptr)
break;
}
return line;
}
//
// FSCANF helps to handle fscanf failures.
// Its do-while block avoids the ambiguity when
// if (...)
// FSCANF();
// is used
//
#define FSCANF(_stream, _format, _var) do{ if (fscanf(_stream, _format, _var) != 1) return false; }while(0)
bool read_model_header(FILE *fp, svm_model* model)
{
svm_parameter& param = model->param;
char cmd[81];
while(true)
{
FSCANF(fp,"%80s",cmd);
if(strcmp(cmd,"svm_type")==0)
{
FSCANF(fp,"%80s",cmd);
int i;
for(i=0;svm_type_table[i];i++)
{
if(strcmp(svm_type_table[i],cmd)==0)
{
param.svm_type=i;
break;
}
}
if(svm_type_table[i] == nullptr)
{
fprintf(stderr,"unknown svm type.\n");
return false;
}
}
else if(strcmp(cmd,"kernel_type")==0)
{
FSCANF(fp,"%80s",cmd);
int i;
for(i=0;kernel_type_table[i];i++)
{
if(strcmp(kernel_type_table[i],cmd)==0)
{
param.kernel_type=i;
break;
}
}
if(kernel_type_table[i] == nullptr)
{
fprintf(stderr,"unknown kernel function.\n");
return false;
}
}
else if(strcmp(cmd,"degree")==0)
FSCANF(fp,"%d",&param.degree);
else if(strcmp(cmd,"gamma")==0)
FSCANF(fp,"%lf",&param.gamma);
else if(strcmp(cmd,"coef0")==0)
FSCANF(fp,"%lf",&param.coef0);
else if(strcmp(cmd,"nr_class")==0)
FSCANF(fp,"%d",&model->nr_class);
else if(strcmp(cmd,"total_sv")==0)
FSCANF(fp,"%d",&model->l);
else if(strcmp(cmd,"rho")==0)
{
int n = model->nr_class * (model->nr_class-1)/2;
model->rho = Malloc(double,n);
for(int i=0;i<n;i++)
FSCANF(fp,"%lf",&model->rho[i]);
}
else if(strcmp(cmd,"label")==0)
{
int n = model->nr_class;
model->label = Malloc(int,n);
for(int i=0;i<n;i++)
FSCANF(fp,"%d",&model->label[i]);
}
else if(strcmp(cmd,"probA")==0)
{
int n = model->nr_class * (model->nr_class-1)/2;
model->probA = Malloc(double,n);
for(int i=0;i<n;i++)
FSCANF(fp,"%lf",&model->probA[i]);
}
else if(strcmp(cmd,"probB")==0)
{
int n = model->nr_class * (model->nr_class-1)/2;
model->probB = Malloc(double,n);
for(int i=0;i<n;i++)
FSCANF(fp,"%lf",&model->probB[i]);
}
else if(strcmp(cmd,"nr_sv")==0)
{
int n = model->nr_class;
model->nSV = Malloc(int,n);
for(int i=0;i<n;i++)
FSCANF(fp,"%d",&model->nSV[i]);
}
else if(strcmp(cmd,"SV")==0)
{
while(true)
{
int c = getc(fp);
if(c==EOF || c=='\n') break;
}
break;
}
else
{
fprintf(stderr,"unknown text in model file: [%s]\n",cmd);
return false;
}
}
return true;
}
svm_model *svm_load_model(const char *model_file_name)
{
FILE *fp = fopen(model_file_name,"rb");
if(fp==nullptr) return nullptr;
mitk::LocaleSwitch localeSwitch("C");
// read parameters
auto *model = Malloc(svm_model,1);
model->rho = nullptr;
model->probA = nullptr;
model->probB = nullptr;
model->sv_indices = nullptr;
model->label = nullptr;
model->nSV = nullptr;
// read header
if (!read_model_header(fp, model))
{
fprintf(stderr, "ERROR: fscanf failed to read model\n");
free(model->rho);
free(model->label);
free(model->nSV);
free(model);
return nullptr;
}
// read sv_coef and SV
int elements = 0;
long pos = ftell(fp);
max_line_len = 1024;
line = Malloc(char,max_line_len);
char *p,*endptr,*idx,*val;
while(readline(fp)!=nullptr)
{
p = strtok(line,":");
while(true)
{
p = strtok(nullptr,":");
if(p == nullptr)
break;
++elements;
}
}
elements += model->l;
fseek(fp,pos,SEEK_SET);
int m = model->nr_class - 1;
int l = model->l;
model->sv_coef = Malloc(double *,m);
int i;
for(i=0;i<m;i++)
model->sv_coef[i] = Malloc(double,l);
model->SV = Malloc(svm_node*,l);
svm_node *x_space = nullptr;
if(l>0) x_space = Malloc(svm_node,elements);
int j=0;
for(i=0;i<l;i++)
{
readline(fp);
model->SV[i] = &x_space[j];
p = strtok(line, " \t");
model->sv_coef[0][i] = strtod(p,&endptr);
for(int k=1;k<m;k++)
{
p = strtok(nullptr, " \t");
model->sv_coef[k][i] = strtod(p,&endptr);
}
while(true)
{
idx = strtok(nullptr, ":");
val = strtok(nullptr, " \t");
if(val == nullptr)
break;
x_space[j].index = (int) strtol(idx,&endptr,10);
x_space[j].value = strtod(val,&endptr);
++j;
}
x_space[j++].index = -1;
}
free(line);
if (ferror(fp) != 0 || fclose(fp) != 0)
return nullptr;
model->free_sv = 1; // XXX
return model;
}
void svm_free_model_content(svm_model* model_ptr)
{
if(model_ptr->free_sv && model_ptr->l > 0 && model_ptr->SV != nullptr)
free((void *)(model_ptr->SV[0]));
if(model_ptr->sv_coef)
{
for(int i=0;i<model_ptr->nr_class-1;i++)
free(model_ptr->sv_coef[i]);
}
free(model_ptr->SV);
model_ptr->SV = nullptr;
free(model_ptr->sv_coef);
model_ptr->sv_coef = nullptr;
free(model_ptr->rho);
model_ptr->rho = nullptr;
free(model_ptr->label);
model_ptr->label= nullptr;
free(model_ptr->probA);
model_ptr->probA = nullptr;
free(model_ptr->probB);
model_ptr->probB= nullptr;
free(model_ptr->sv_indices);
model_ptr->sv_indices = nullptr;
free(model_ptr->nSV);
model_ptr->nSV = nullptr;
}
void svm_free_and_destroy_model(svm_model** model_ptr_ptr)
{
if(model_ptr_ptr != nullptr && *model_ptr_ptr != nullptr)
{
svm_free_model_content(*model_ptr_ptr);
free(*model_ptr_ptr);
*model_ptr_ptr = nullptr;
}
}
void svm_destroy_param(svm_parameter* param)
{
free(param->weight_label);
free(param->weight);
}
const char *svm_check_parameter(const svm_problem *prob, const svm_parameter *param)
{
// svm_type
int svm_type = param->svm_type;
if(svm_type != C_SVC &&
svm_type != NU_SVC &&
svm_type != ONE_CLASS &&
svm_type != EPSILON_SVR &&
svm_type != NU_SVR)
return "unknown svm type";
// kernel_type, degree
int kernel_type = param->kernel_type;
if(kernel_type != LINEAR &&
kernel_type != POLY &&
kernel_type != RBF &&
kernel_type != SIGMOID &&
kernel_type != PRECOMPUTED)
return "unknown kernel type";
if(param->gamma < 0)
return "gamma < 0";
if(param->degree < 0)
return "degree of polynomial kernel < 0";
// cache_size,eps,C,nu,p,shrinking
if(param->cache_size <= 0)
return "cache_size <= 0";
if(param->eps <= 0)
return "eps <= 0";
if(svm_type == C_SVC ||
svm_type == EPSILON_SVR ||
svm_type == NU_SVR)
if(param->C <= 0)
return "C <= 0";
if(svm_type == NU_SVC ||
svm_type == ONE_CLASS ||
svm_type == NU_SVR)
if(param->nu <= 0 || param->nu > 1)
return "nu <= 0 or nu > 1";
if(svm_type == EPSILON_SVR)
if(param->p < 0)
return "p < 0";
if(param->shrinking != 0 &&
param->shrinking != 1)
return "shrinking != 0 and shrinking != 1";
if(param->probability != 0 &&
param->probability != 1)
return "probability != 0 and probability != 1";
if(param->probability == 1 &&
svm_type == ONE_CLASS)
return "one-class SVM probability output not supported yet";
// check whether nu-svc is feasible
if(svm_type == NU_SVC)
{
int l = prob->l;
int max_nr_class = 16;
int nr_class = 0;
auto *label = Malloc(int,max_nr_class);
auto *count = Malloc(double,max_nr_class);
int i;
for(i=0;i<l;i++)
{
auto this_label = (int)prob->y[i];
int j;
for(j=0;j<nr_class;j++)
if(this_label == label[j])
{
count[j] += prob->W[i];
break;
}
if(j == nr_class)
{
if(nr_class == max_nr_class)
{
max_nr_class *= 2;
label = (int *)realloc(label,max_nr_class*sizeof(int));
count = (double *)realloc(count,max_nr_class*sizeof(double));
}
label[nr_class] = this_label;
count[nr_class] = prob->W[i];
++nr_class;
}
}
for(i=0;i<nr_class;i++)
{
double n1 = count[i];
for(int j=i+1;j<nr_class;j++)
{
double n2 = count[j];
if(param->nu*(n1+n2)/2 > min(n1,n2))
{
free(label);
free(count);
return "specified nu is infeasible";
}
}
}
free(label);
free(count);
}
return nullptr;
}
int svm_check_probability_model(const svm_model *model)
{
return ((model->param.svm_type == C_SVC || model->param.svm_type == NU_SVC) &&
model->probA!=nullptr && model->probB!=nullptr) ||
((model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) &&
model->probA!=nullptr);
}
void svm_set_print_string_function(void (*print_func)(const char *))
{
if(print_func == nullptr)
svm_print_string = &print_string_stdout;
else
svm_print_string = print_func;
}
diff --git a/Modules/Classification/CLMiniApps/CMakeLists.txt b/Modules/Classification/CLMiniApps/CMakeLists.txt
index fb5bb3b6c8..7325e45a38 100644
--- a/Modules/Classification/CLMiniApps/CMakeLists.txt
+++ b/Modules/Classification/CLMiniApps/CMakeLists.txt
@@ -1,117 +1,117 @@
option(BUILD_ClassificationMiniApps "Build commandline tools for classification" OFF)
if(BUILD_ClassificationMiniApps OR MITK_BUILD_ALL_APPS)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
# list of miniapps
# if an app requires additional dependencies
# they are added after a "^^" and separated by "_"
set( classificationminiapps
RandomForestTraining^^MitkCLVigraRandomForest
NativeHeadCTSegmentation^^MitkCLVigraRandomForest
ManualSegmentationEvaluation^^MitkCLVigraRandomForest
CLScreenshot^^MitkCore_MitkQtWidgetsExt_MitkCLUtilities
CLDicom2Nrrd^^MitkCore
CLResampleImageToReference^^MitkCore
CLGlobalImageFeatures^^MitkCLUtilities_MitkQtWidgetsExt
CLMRNormalization^^MitkCLUtilities_MitkCLMRUtilities
CLStaple^^MitkCLUtilities
CLVoxelFeatures^^MitkCLUtilities
CLPolyToNrrd^^
CLPlanarFigureToNrrd^^MitkCore_MitkSegmentation_MitkMultilabel
CLSimpleVoxelClassification^^MitkDataCollection_MitkCLVigraRandomForest
CLVoxelClassification^^MitkDataCollection_MitkCLImportanceWeighting_MitkCLVigraRandomForest
CLBrainMask^^MitkCLUtilities
XRaxSimulationFromCT^^MitkCLUtilities
CLRandomSampling^^MitkCore_MitkCLUtilities
CLRemoveEmptyVoxels^^MitkCore
CLN4^^MitkCore
CLSkullMask^^MitkCore
CLPointSetToSegmentation^^
CLMultiForestPrediction^^MitkDataCollection_MitkCLVigraRandomForest
CLNrrdToPoly^^MitkCore
CL2Dto3DImage^^MitkCore
CLWeighting^^MitkCore_MitkCLImportanceWeighting_MitkCLUtilities
CLOverlayRoiCenterOfMass^^MitkCore_MitkCLUtilities_MitkQtWidgetsExt
CLLungSegmentation^^MitkCore_MitkSegmentation_MitkMultilabel
)
foreach(classificationminiapps ${classificationminiapps})
# extract mini app name and dependencies
string(REPLACE "^^" "\\;" miniapp_info ${classificationminiapps})
set(miniapp_info_list ${miniapp_info})
list(GET miniapp_info_list 0 appname)
list(GET miniapp_info_list 1 raw_dependencies)
string(REPLACE "_" "\\;" dependencies "${raw_dependencies}")
set(dependencies_list ${dependencies})
mitk_create_executable(${appname}
DEPENDS MitkCore MitkCLCore MitkCommandLine ${dependencies_list}
- PACKAGE_DEPENDS Qt5|Core Vigra VTK|IOImage
+ PACKAGE_DEPENDS Qt5|Core Vigra VTK|IOImage ITK|Smoothing
CPP_FILES ${appname}.cpp
)
if(EXECUTABLE_IS_ENABLED)
# On Linux, create a shell script to start a relocatable application
if(UNIX AND NOT APPLE)
install(PROGRAMS "${MITK_SOURCE_DIR}/CMake/RunInstalledApp.sh" DESTINATION "." RENAME ${EXECUTABLE_TARGET}.sh)
endif()
get_target_property(_is_bundle ${EXECUTABLE_TARGET} MACOSX_BUNDLE)
if(APPLE)
if(_is_bundle)
set(_target_locations ${EXECUTABLE_TARGET}.app)
set(${_target_locations}_qt_plugins_install_dir ${EXECUTABLE_TARGET}.app/Contents/MacOS)
set(_bundle_dest_dir ${EXECUTABLE_TARGET}.app/Contents/MacOS)
set(_qt_plugins_for_current_bundle ${EXECUTABLE_TARGET}.app/Contents/MacOS)
set(_qt_conf_install_dirs ${EXECUTABLE_TARGET}.app/Contents/Resources)
install(TARGETS ${EXECUTABLE_TARGET} BUNDLE DESTINATION . )
else()
if(NOT MACOSX_BUNDLE_NAMES)
set(_qt_conf_install_dirs bin)
set(_target_locations bin/${EXECUTABLE_TARGET})
set(${_target_locations}_qt_plugins_install_dir bin)
install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION bin)
else()
foreach(bundle_name ${MACOSX_BUNDLE_NAMES})
list(APPEND _qt_conf_install_dirs ${bundle_name}.app/Contents/Resources)
set(_current_target_location ${bundle_name}.app/Contents/MacOS/${EXECUTABLE_TARGET})
list(APPEND _target_locations ${_current_target_location})
set(${_current_target_location}_qt_plugins_install_dir ${bundle_name}.app/Contents/MacOS)
message( " set(${_current_target_location}_qt_plugins_install_dir ${bundle_name}.app/Contents/MacOS) ")
install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION ${bundle_name}.app/Contents/MacOS/)
endforeach()
endif()
endif()
else()
set(_target_locations bin/${EXECUTABLE_TARGET}${CMAKE_EXECUTABLE_SUFFIX})
set(${_target_locations}_qt_plugins_install_dir bin)
set(_qt_conf_install_dirs bin)
install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION bin)
endif()
endif()
endforeach()
mitk_create_executable(CLMatchPointReg
DEPENDS MitkCore MitkCLUtilities MitkMatchPointRegistration MitkCommandLine MitkMatchPointRegistrationUI
PACKAGE_DEPENDS Qt5|Core Vigra MatchPoint
CPP_FILES CLMatchPointReg.cpp
)
# On Linux, create a shell script to start a relocatable application
if(UNIX AND NOT APPLE)
install(PROGRAMS "${MITK_SOURCE_DIR}/CMake/RunInstalledApp.sh" DESTINATION "." RENAME ${EXECUTABLE_TARGET}.sh)
endif()
if(EXECUTABLE_IS_ENABLED)
MITK_INSTALL_TARGETS(EXECUTABLES ${EXECUTABLE_TARGET})
endif()
endif()
diff --git a/Modules/Classification/CLMiniApps/ManualSegmentationEvaluation.cpp b/Modules/Classification/CLMiniApps/ManualSegmentationEvaluation.cpp
index 4339aa228f..92ffbb677f 100644
--- a/Modules/Classification/CLMiniApps/ManualSegmentationEvaluation.cpp
+++ b/Modules/Classification/CLMiniApps/ManualSegmentationEvaluation.cpp
@@ -1,360 +1,364 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <mitkCoreObjectFactory.h>
#include "mitkImage.h"
#include <mitkLexicalCast.h>
#include <vnl/vnl_random.h>
#include "mitkCommandLineParser.h"
#include <mitkIOUtil.h>
#include <itksys/SystemTools.hxx>
#include <mitkITKImageImport.h>
#include <mitkImageCast.h>
#include <mitkProperties.h>
// ITK
#include <itkImageRegionIterator.h>
// MITK
#include <mitkIOUtil.h>
// Classification
#include <mitkCLUtil.h>
#include <mitkVigraRandomForestClassifier.h>
#include <QDir>
#include <QString>
#include <QStringList>
#include <itkLabelOverlapMeasuresImageFilter.h>
#include <itkImageRegionIteratorWithIndex.h>
#include <itkNeighborhoodFunctorImageFilter.h>
#include <itkFirstOrderStatisticsFeatureFunctor.h>
#include <itkNeighborhood.h>
#include <itkHessianMatrixEigenvalueImageFilter.h>
#include <itkStructureTensorEigenvalueImageFilter.h>
#include <itkLineHistogramBasedMassImageFilter.h>
+#include <random>
+
using namespace mitk;
std::vector<mitk::Image::Pointer> m_FeatureImageVector;
void ProcessFeatureImages(const mitk::Image::Pointer & raw_image, const mitk::Image::Pointer & brain_mask)
{
typedef itk::Image<double,3> DoubleImageType;
typedef itk::Image<short,3> ShortImageType;
typedef itk::ConstNeighborhoodIterator<DoubleImageType> NeighborhoodType; // Neighborhood iterator to access image
typedef itk::Functor::NeighborhoodFirstOrderStatistics<NeighborhoodType, double> FunctorType;
typedef itk::NeighborhoodFunctorImageFilter<DoubleImageType, DoubleImageType, FunctorType> FOSFilerType;
m_FeatureImageVector.clear();
// RAW
m_FeatureImageVector.push_back(raw_image);
// GAUSS
mitk::Image::Pointer smoothed;
mitk::CLUtil::GaussianFilter(raw_image,smoothed,1);
m_FeatureImageVector.push_back(smoothed);
// Calculate Probability maps (parameters used from literatur)
// CSF
mitk::Image::Pointer csf_prob = mitk::Image::New();
mitk::CLUtil::ProbabilityMap(smoothed,13.9, 8.3,csf_prob);
m_FeatureImageVector.push_back(csf_prob);
// Lesion
mitk::Image::Pointer les_prob = mitk::Image::New();
mitk::CLUtil::ProbabilityMap(smoothed,59, 11.6,les_prob);
m_FeatureImageVector.push_back(les_prob);
// Barin (GM/WM)
mitk::Image::Pointer brain_prob = mitk::Image::New();
mitk::CLUtil::ProbabilityMap(smoothed,32, 5.6,brain_prob);
m_FeatureImageVector.push_back(brain_prob);
std::vector<unsigned int> FOS_sizes;
FOS_sizes.push_back(1);
DoubleImageType::Pointer input;
ShortImageType::Pointer mask;
mitk::CastToItkImage(smoothed, input);
mitk::CastToItkImage(brain_mask, mask);
for(unsigned int i = 0 ; i < FOS_sizes.size(); i++)
{
FOSFilerType::Pointer filter = FOSFilerType::New();
filter->SetNeighborhoodSize(FOS_sizes[i]);
filter->SetInput(input);
filter->SetMask(mask);
filter->Update();
FOSFilerType::DataObjectPointerArray array = filter->GetOutputs();
for( unsigned int i = 0; i < FunctorType::OutputCount; i++)
{
mitk::Image::Pointer featureimage;
mitk::CastToMitkImage(dynamic_cast<DoubleImageType *>(array[i].GetPointer()),featureimage);
m_FeatureImageVector.push_back(featureimage);
// AddImageAsDataNode(featureimage,FunctorType::GetFeatureName(i))->SetVisibility(show_nodes);
}
}
{
itk::HessianMatrixEigenvalueImageFilter< DoubleImageType >::Pointer filter = itk::HessianMatrixEigenvalueImageFilter< DoubleImageType >::New();
filter->SetInput(input);
filter->SetImageMask(mask);
filter->SetSigma(3);
filter->Update();
mitk::Image::Pointer o1,o2,o3;
mitk::CastToMitkImage(filter->GetOutput(0),o1);
mitk::CastToMitkImage(filter->GetOutput(1),o2);
mitk::CastToMitkImage(filter->GetOutput(2),o3);
m_FeatureImageVector.push_back(o1);
m_FeatureImageVector.push_back(o2);
m_FeatureImageVector.push_back(o3);
}
{
itk::StructureTensorEigenvalueImageFilter< DoubleImageType >::Pointer filter = itk::StructureTensorEigenvalueImageFilter< DoubleImageType >::New();
filter->SetInput(input);
filter->SetImageMask(mask);
filter->SetInnerScale(1.5);
filter->SetOuterScale(3);
filter->Update();
mitk::Image::Pointer o1,o2,o3;
mitk::CastToMitkImage(filter->GetOutput(0),o1);
mitk::CastToMitkImage(filter->GetOutput(1),o2);
mitk::CastToMitkImage(filter->GetOutput(2),o3);
m_FeatureImageVector.push_back(o1);
m_FeatureImageVector.push_back(o2);
m_FeatureImageVector.push_back(o3);
}
{
itk::LineHistogramBasedMassImageFilter< DoubleImageType >::Pointer filter = itk::LineHistogramBasedMassImageFilter< DoubleImageType >::New();
filter->SetInput(input);
filter->SetImageMask(mask);
filter->Update();
mitk::Image::Pointer o1;
mitk::CastToMitkImage(filter->GetOutput(0),o1);
m_FeatureImageVector.push_back(o1);
}
}
std::vector<mitk::Point3D> PointSetToVector(const mitk::PointSet::Pointer & mps)
{
std::vector<mitk::Point3D> result;
for(int i = 0 ; i < mps->GetSize(); i++)
result.push_back(mps->GetPoint(i));
return result;
}
int main(int argc, char* argv[])
{
mitkCommandLineParser parser;
parser.setArgumentPrefix("--", "-");
// required params
parser.addArgument("inputdir", "i", mitkCommandLineParser::Directory, "Input Directory", "Contains input feature files.", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("outputdir", "o", mitkCommandLineParser::Directory, "Output Directory", "Destination of output files.", us::Any(), false, false, false, mitkCommandLineParser::Output);
parser.addArgument("mitkprojectdata", "d", mitkCommandLineParser::File, "original class mask and raw image", "Orig. data.", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("csfmps", "csf", mitkCommandLineParser::File, "CSF Pointset", ".", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("lesmps", "les", mitkCommandLineParser::File, "LES Pointset", ".", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("bramps", "bra", mitkCommandLineParser::File, "BRA Pointset", ".", us::Any(), false, false, false, mitkCommandLineParser::Input);
// parser.addArgument("points", "p", mitkCommandLineParser::Int, "Ensure that p points are selected", ".", us::Any(), false);
// Miniapp Infos
parser.setCategory("Classification Tools");
parser.setTitle("Evaluationtool for Manual-Segmentation");
parser.setDescription("Uses Datacollection to calculate DICE scores for CSF LES BRA");
parser.setContributor("German Cancer Research Center (DKFZ)");
// Params parsing
std::map<std::string, us::Any> parsedArgs = parser.parseArguments(argc, argv);
if (parsedArgs.size()==0)
return EXIT_FAILURE;
std::string inputdir = us::any_cast<std::string>(parsedArgs["inputdir"]);
std::string outputdir = us::any_cast<std::string>(parsedArgs["outputdir"]);
std::string mitkprojectdata = us::any_cast<std::string>(parsedArgs["mitkprojectdata"]);
std::string csf_mps_name = us::any_cast<std::string>(parsedArgs["csfmps"]);
std::string les_mps_name = us::any_cast<std::string>(parsedArgs["lesmps"]);
std::string bra_mps_name = us::any_cast<std::string>(parsedArgs["bramps"]);
mitk::Image::Pointer class_mask_sampled, raw_image, class_mask;
mitk::PointSet::Pointer CSF_mps, LES_mps, BRA_mps;
// Load from mitk-project
auto so = mitk::IOUtil::Load(inputdir + "/" + mitkprojectdata);
std::map<unsigned int, unsigned int> map;
mitk::CLUtil::CountVoxel(dynamic_cast<mitk::Image *>(so[1].GetPointer()), map);
raw_image = map.size() <= 7 ? dynamic_cast<mitk::Image *>(so[0].GetPointer()) : dynamic_cast<mitk::Image *>(so[1].GetPointer());
class_mask = map.size() <= 7 ? dynamic_cast<mitk::Image *>(so[1].GetPointer()) : dynamic_cast<mitk::Image *>(so[0].GetPointer());
CSF_mps = mitk::IOUtil::Load<mitk::PointSet>(inputdir + "/" + csf_mps_name);
LES_mps = mitk::IOUtil::Load<mitk::PointSet>(inputdir + "/" + les_mps_name);
BRA_mps = mitk::IOUtil::Load<mitk::PointSet>(inputdir + "/" + bra_mps_name);
unsigned int num_points = CSF_mps->GetSize() + LES_mps->GetSize() + BRA_mps->GetSize();
MITK_INFO << "Found #" << num_points << " points over all classes.";
ProcessFeatureImages(raw_image, class_mask);
std::map<unsigned int, unsigned int> tmpMap;
tmpMap[0] = 0;
tmpMap[1] = 1;
tmpMap[2] = 1;
tmpMap[3] = 1;
tmpMap[4] = 2;
tmpMap[5] = 3;
tmpMap[6] = 3;
mitk::CLUtil::MergeLabels( class_mask, tmpMap);
class_mask_sampled = class_mask->Clone();
itk::Image<short,3>::Pointer itk_classmask_sampled;
mitk::CastToItkImage(class_mask_sampled,itk_classmask_sampled);
itk::ImageRegionIteratorWithIndex<itk::Image<short,3> >::IndexType index;
itk::ImageRegionIteratorWithIndex<itk::Image<short,3> > iit(itk_classmask_sampled,itk_classmask_sampled->GetLargestPossibleRegion());
std::ofstream myfile;
myfile.open (inputdir + "/results_3.csv");
Eigen::MatrixXd X_test;
unsigned int count_test = 0;
mitk::CLUtil::CountVoxel(class_mask, count_test);
X_test = Eigen::MatrixXd(count_test, m_FeatureImageVector.size());
unsigned int pos = 0;
for( const auto & image : m_FeatureImageVector)
{
X_test.col(pos) = mitk::CLUtil::Transform<double>(image,class_mask);
++pos;
}
+ std::random_device rnd;
+
unsigned int runs = 20;
for(unsigned int k = 0 ; k < runs; k++)
{
auto CSF_vec = PointSetToVector(CSF_mps);
auto LES_vec = PointSetToVector(LES_mps);
auto BRA_vec = PointSetToVector(BRA_mps);
itk_classmask_sampled->FillBuffer(0);
// initial draws
- std::random_shuffle(CSF_vec.begin(), CSF_vec.end());
+ std::shuffle(CSF_vec.begin(), CSF_vec.end(), std::mt19937(rnd()));
class_mask->GetGeometry()->WorldToIndex(CSF_vec.back(),index);
iit.SetIndex(index);
iit.Set(1);
CSF_vec.pop_back();
- std::random_shuffle(LES_vec.begin(), LES_vec.end());
+ std::shuffle(LES_vec.begin(), LES_vec.end(), std::mt19937(rnd()));
class_mask->GetGeometry()->WorldToIndex(LES_vec.back(),index);
iit.SetIndex(index);
iit.Set(2);
LES_vec.pop_back();
- std::random_shuffle(BRA_vec.begin(), BRA_vec.end());
+ std::shuffle(BRA_vec.begin(), BRA_vec.end(), std::mt19937(rnd()));
class_mask->GetGeometry()->WorldToIndex(BRA_vec.back(),index);
iit.SetIndex(index);
iit.Set(3);
BRA_vec.pop_back();
std::stringstream ss;
while(!(CSF_vec.empty() && LES_vec.empty() && BRA_vec.empty()))
{
mitk::CastToMitkImage(itk_classmask_sampled, class_mask_sampled);
// Train forest
mitk::VigraRandomForestClassifier::Pointer classifier = mitk::VigraRandomForestClassifier::New();
classifier->SetTreeCount(40);
classifier->SetSamplesPerTree(0.66);
Eigen::MatrixXd X_train;
unsigned int count_train = 0;
mitk::CLUtil::CountVoxel(class_mask_sampled, count_train);
X_train = Eigen::MatrixXd(count_train, m_FeatureImageVector.size() );
unsigned int pos = 0;
for( const auto & image : m_FeatureImageVector)
{
X_train.col(pos) = mitk::CLUtil::Transform<double>(image,class_mask_sampled);
++pos;
}
Eigen::MatrixXi Y = mitk::CLUtil::Transform<int>(class_mask_sampled,class_mask_sampled);
classifier->Train(X_train,Y);
Eigen::MatrixXi Y_test = classifier->Predict(X_test);
mitk::Image::Pointer result_mask = mitk::CLUtil::Transform<int>(Y_test, class_mask);
itk::Image<short,3>::Pointer itk_result_mask, itk_class_mask;
mitk::CastToItkImage(result_mask,itk_result_mask);
mitk::CastToItkImage(class_mask, itk_class_mask);
itk::LabelOverlapMeasuresImageFilter<itk::Image<short,3> >::Pointer overlap_filter = itk::LabelOverlapMeasuresImageFilter<itk::Image<short,3> >::New();
overlap_filter->SetInput(0,itk_result_mask);
overlap_filter->SetInput(1,itk_class_mask);
overlap_filter->Update();
MITK_INFO << "DICE (" << num_points - (CSF_vec.size() + LES_vec.size() + BRA_vec.size()) << "): " << overlap_filter->GetDiceCoefficient();
ss << overlap_filter->GetDiceCoefficient() <<",";
// random class selection
if(!CSF_vec.empty())
{
- std::random_shuffle(CSF_vec.begin(), CSF_vec.end());
+ std::shuffle(CSF_vec.begin(), CSF_vec.end(), std::mt19937(rnd()));
class_mask->GetGeometry()->WorldToIndex(CSF_vec.back(),index);
iit.SetIndex(index);
iit.Set(1);
CSF_vec.pop_back();
}
if(!LES_vec.empty())
{
- std::random_shuffle(LES_vec.begin(), LES_vec.end());
+ std::shuffle(LES_vec.begin(), LES_vec.end(), std::mt19937(rnd()));
class_mask->GetGeometry()->WorldToIndex(LES_vec.back(),index);
iit.SetIndex(index);
iit.Set(2);
LES_vec.pop_back();
}
if(!BRA_vec.empty())
{
- std::random_shuffle(BRA_vec.begin(), BRA_vec.end());
+ std::shuffle(BRA_vec.begin(), BRA_vec.end(), std::mt19937(rnd()));
class_mask->GetGeometry()->WorldToIndex(BRA_vec.back(),index);
iit.SetIndex(index);
iit.Set(3);
BRA_vec.pop_back();
}
}
myfile << ss.str() << "\n";
myfile.flush();
}
myfile.close();
return EXIT_SUCCESS;
}
diff --git a/Modules/Classification/CLUtilities/CMakeLists.txt b/Modules/Classification/CLUtilities/CMakeLists.txt
index 0f05dd8069..577381576a 100644
--- a/Modules/Classification/CLUtilities/CMakeLists.txt
+++ b/Modules/Classification/CLUtilities/CMakeLists.txt
@@ -1,10 +1,10 @@
mitk_create_module(
DEPENDS MitkCore MitkCLCore MitkCommandLine MitkDICOM
- PACKAGE_DEPENDS PUBLIC Eigen OpenMP PRIVATE tinyxml2 VTK|FiltersStatistics
+ PACKAGE_DEPENDS PUBLIC Eigen OpenMP PRIVATE tinyxml2 ITK|MathematicalMorphology+Smoothing VTK|FiltersStatistics
)
if(TARGET ${MODULE_TARGET})
if(BUILD_TESTING)
add_subdirectory(test)
endif()
endif()
diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthMatrixFilter.hxx b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthMatrixFilter.hxx
index 7057b02697..8775a8aef6 100644
--- a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthMatrixFilter.hxx
+++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthMatrixFilter.hxx
@@ -1,425 +1,411 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
/*=========================================================================
*
* Copyright Insight Software Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/
#ifndef __itkEnhancedScalarImageToRunLengthMatrixFilter_hxx
#define __itkEnhancedScalarImageToRunLengthMatrixFilter_hxx
#include "itkEnhancedScalarImageToRunLengthMatrixFilter.h"
#include "itkConstNeighborhoodIterator.h"
#include "itkNeighborhood.h"
#include "vnl/vnl_math.h"
#include "itkMacro.h"
namespace itk
{
namespace Statistics
{
template<typename TImageType, typename THistogramFrequencyContainer>
EnhancedScalarImageToRunLengthMatrixFilter<TImageType, THistogramFrequencyContainer>
::EnhancedScalarImageToRunLengthMatrixFilter() :
m_NumberOfBinsPerAxis( itkGetStaticConstMacro( DefaultBinsPerAxis ) ),
m_Min( NumericTraits<PixelType>::NonpositiveMin() ),
m_Max( NumericTraits<PixelType>::max() ),
m_MinDistance( NumericTraits<RealType>::ZeroValue() ),
m_MaxDistance( NumericTraits<RealType>::max() ),
m_InsidePixelValue( NumericTraits<PixelType>::OneValue() )
{
this->SetNumberOfRequiredInputs( 1 );
this->SetNumberOfRequiredOutputs( 1 );
const unsigned int measurementVectorSize = 2;
this->ProcessObject::SetNthOutput( 0, this->MakeOutput( 0 ) );
HistogramType *output = const_cast<HistogramType *>( this->GetOutput() );
output->SetMeasurementVectorSize( measurementVectorSize );
this->m_LowerBound.SetSize( measurementVectorSize );
this->m_UpperBound.SetSize( measurementVectorSize );
this->m_LowerBound[0] = this->m_Min;
this->m_LowerBound[1] = this->m_MinDistance;
this->m_UpperBound[0] = this->m_Max;
this->m_UpperBound[1] = this->m_MaxDistance;
}
template<typename TImageType, typename THistogramFrequencyContainer>
void
EnhancedScalarImageToRunLengthMatrixFilter<TImageType, THistogramFrequencyContainer>
::SetOffset( const OffsetType offset )
{
OffsetVectorPointer offsetVector = OffsetVector::New();
offsetVector->push_back( offset );
this->SetOffsets( offsetVector );
}
template<typename TImageType, typename THistogramFrequencyContainer>
void
EnhancedScalarImageToRunLengthMatrixFilter<TImageType, THistogramFrequencyContainer>
::SetInput( const ImageType *image )
{
// Process object is not const-correct so the const_cast is required here
this->ProcessObject::SetNthInput( 0, const_cast<ImageType *>( image ) );
}
template<typename TImageType, typename THistogramFrequencyContainer>
void
EnhancedScalarImageToRunLengthMatrixFilter<TImageType, THistogramFrequencyContainer>
::SetMaskImage( const ImageType *image )
{
// Process object is not const-correct so the const_cast is required here
this->ProcessObject::SetNthInput( 1, const_cast<ImageType *>( image ) );
}
template<typename TImageType, typename THistogramFrequencyContainer>
const TImageType *
EnhancedScalarImageToRunLengthMatrixFilter<TImageType, THistogramFrequencyContainer>
::GetInput() const
{
if( this->GetNumberOfInputs() < 1 )
{
return ITK_NULLPTR;
}
return static_cast<const ImageType *>( this->ProcessObject::GetInput( 0 ) );
}
template<typename TImageType, typename THistogramFrequencyContainer>
const TImageType *
EnhancedScalarImageToRunLengthMatrixFilter<TImageType, THistogramFrequencyContainer>
::GetMaskImage() const
{
if( this->GetNumberOfInputs() < 2 )
{
return ITK_NULLPTR;
}
return static_cast<const ImageType *>( this->ProcessObject::GetInput( 1 ) );
}
template<typename TImageType, typename THistogramFrequencyContainer>
const typename EnhancedScalarImageToRunLengthMatrixFilter<TImageType,
THistogramFrequencyContainer >::HistogramType *
EnhancedScalarImageToRunLengthMatrixFilter<TImageType, THistogramFrequencyContainer>
::GetOutput() const
{
const HistogramType *output =
static_cast<const HistogramType *>( this->ProcessObject::GetOutput( 0 ) );
return output;
}
template<typename TImageType, typename THistogramFrequencyContainer>
typename EnhancedScalarImageToRunLengthMatrixFilter<TImageType,
THistogramFrequencyContainer>::DataObjectPointer
EnhancedScalarImageToRunLengthMatrixFilter<TImageType, THistogramFrequencyContainer>
::MakeOutput( DataObjectPointerArraySizeType itkNotUsed( idx ) )
{
return HistogramType::New().GetPointer();
}
template<typename TImageType, typename THistogramFrequencyContainer>
void
EnhancedScalarImageToRunLengthMatrixFilter<TImageType, THistogramFrequencyContainer>
::GenerateData()
{
HistogramType *output =
static_cast<HistogramType *>( this->ProcessObject::GetOutput( 0 ) );
const ImageType * inputImage = this->GetInput();
// First, create an appropriate histogram with the right number of bins
// and mins and maxes correct for the image type.
typename HistogramType::SizeType size( output->GetMeasurementVectorSize() );
size.Fill( this->m_NumberOfBinsPerAxis );
this->m_LowerBound[0] = this->m_Min;
this->m_LowerBound[1] = this->m_MinDistance;
this->m_UpperBound[0] = this->m_Max;
this->m_UpperBound[1] = this->m_MaxDistance;
output->Initialize( size, this->m_LowerBound, this->m_UpperBound );
MeasurementVectorType run( output->GetMeasurementVectorSize() );
typename HistogramType::IndexType hIndex;
// Iterate over all of those pixels and offsets, adding each
// distance/intensity pair to the histogram
typedef ConstNeighborhoodIterator<ImageType> NeighborhoodIteratorType;
typename NeighborhoodIteratorType::RadiusType radius;
radius.Fill( 1 );
NeighborhoodIteratorType neighborIt( radius,
inputImage, inputImage->GetRequestedRegion() );
// this temp image has the same dimension for each offset
// moving the allocation out of loop of offsets
// while keeping FillBuffer with boolean false in each loop
typedef Image<bool, ImageDimension> BoolImageType;
typename BoolImageType::Pointer alreadyVisitedImage = BoolImageType::New();
alreadyVisitedImage->CopyInformation( inputImage );
alreadyVisitedImage->SetRegions( inputImage->GetRequestedRegion() );
alreadyVisitedImage->Allocate();
typename OffsetVector::ConstIterator offsets;
for( offsets = this->GetOffsets()->Begin();
offsets != this->GetOffsets()->End(); offsets++ )
{
alreadyVisitedImage->FillBuffer( false );
neighborIt.GoToBegin();
OffsetType offset = offsets.Value();
this->NormalizeOffsetDirection(offset);
for( neighborIt.GoToBegin(); !neighborIt.IsAtEnd(); ++neighborIt )
{
const PixelType centerPixelIntensity = neighborIt.GetCenterPixel();
if (centerPixelIntensity != centerPixelIntensity) // Check for invalid values
{
continue;
}
IndexType centerIndex = neighborIt.GetIndex();
if( centerPixelIntensity < this->m_Min ||
centerPixelIntensity > this->m_Max ||
alreadyVisitedImage->GetPixel( centerIndex ) || ( this->GetMaskImage() &&
this->GetMaskImage()->GetPixel( centerIndex ) !=
this->m_InsidePixelValue ) )
{
continue; // don't put a pixel in the histogram if the value
// is out-of-bounds or is outside the mask.
}
itkDebugMacro("===> offset = " << offset << std::endl);
MeasurementType centerBinMin = this->GetOutput()->
GetBinMinFromValue( 0, centerPixelIntensity );
MeasurementType centerBinMax = this->GetOutput()->
GetBinMaxFromValue( 0, centerPixelIntensity );
MeasurementType lastBinMax = this->GetOutput()->
GetDimensionMaxs( 0 )[ this->GetOutput()->GetSize( 0 ) - 1 ];
PixelType pixelIntensity( NumericTraits<PixelType>::ZeroValue() );
IndexType index;
int steps = 0;
index = centerIndex + offset;
IndexType lastGoodIndex = centerIndex;
bool runLengthSegmentAlreadyVisited = false;
// Scan from the current pixel at index, following
// the direction of offset. Run length is computed as the
// length of continuous pixels whose pixel values are
// in the same bin.
while ( inputImage->GetRequestedRegion().IsInside(index) )
{
pixelIntensity = inputImage->GetPixel(index);
// For the same offset, each run length segment can
// only be visited once
if (alreadyVisitedImage->GetPixel( index ) )
{
runLengthSegmentAlreadyVisited = true;
break;
}
if (pixelIntensity != pixelIntensity)
{
break;
}
// Special attention paid to boundaries of bins.
// For the last bin,
// it is left close and right close (following the previous
// gerrit patch).
// For all
// other bins,
// the bin is left close and right open.
if ( pixelIntensity >= centerBinMin
&& ( pixelIntensity < centerBinMax || ( pixelIntensity == centerBinMax && centerBinMax == lastBinMax ) )
&& (!this->GetMaskImage() || this->GetMaskImage()->GetPixel(index) == this->m_InsidePixelValue))
{
alreadyVisitedImage->SetPixel( index, true );
lastGoodIndex = index;
index += offset;
steps++;
}
else
{
break;
}
}
if ( runLengthSegmentAlreadyVisited )
{
MITK_INFO << "Already visited 1 " << index;
continue;
}
IndexType lastGoodIndex2 = lastGoodIndex;
index = centerIndex - offset;
lastGoodIndex = centerIndex;
while ( inputImage->GetRequestedRegion().IsInside(index) )
{
pixelIntensity = inputImage->GetPixel(index);
if (pixelIntensity != pixelIntensity)
{
break;
}
if (alreadyVisitedImage->GetPixel( index ) )
{
if (pixelIntensity >= centerBinMin
&& (pixelIntensity < centerBinMax || (pixelIntensity == centerBinMax && centerBinMax == lastBinMax)))
{
runLengthSegmentAlreadyVisited = true;
}
break;
}
if ( pixelIntensity >= centerBinMin
&& ( pixelIntensity < centerBinMax || ( pixelIntensity == centerBinMax && centerBinMax == lastBinMax ) )
&& (!this->GetMaskImage() || this->GetMaskImage()->GetPixel(index) == this->m_InsidePixelValue))
{
alreadyVisitedImage->SetPixel( index, true );
lastGoodIndex = index;
steps++;
index -= offset;
}
else
break;
}
if (runLengthSegmentAlreadyVisited)
{
MITK_INFO << "Already visited 2 " << index;
continue;
}
PointType centerPoint;
inputImage->TransformIndexToPhysicalPoint(
centerIndex, centerPoint );
PointType point;
inputImage->TransformIndexToPhysicalPoint( lastGoodIndex, point );
PointType point2;
inputImage->TransformIndexToPhysicalPoint( lastGoodIndex2, point2 );
run[0] = centerPixelIntensity;
run[1] = steps;
//run[1] = point.EuclideanDistanceTo( point2 );
if( run[1] >= this->m_MinDistance && run[1] <= this->m_MaxDistance )
{
output->GetIndex( run, hIndex );
output->IncreaseFrequencyOfIndex( hIndex, 1 );
-
- itkDebugStatement(typename HistogramType::IndexType tempMeasurementIndex;)
- itkDebugStatement(output->GetIndex(run,tempMeasurementIndex);)
- itkDebugMacro( "centerIndex<->index: "
- << static_cast<int>( centerPixelIntensity )
- << "@"<< centerIndex
- << "<->" << static_cast<int>( pixelIntensity ) << "@" << index
- <<", Bin# " << tempMeasurementIndex
- << ", Measurement: (" << run[0] << ", " << run[1] << ")"
- << ", Center bin [" << this->GetOutput()->GetBinMinFromValue( 0, run[0] )
- << "," << this->GetOutput()->GetBinMaxFromValue( 0, run[0] ) << "]"
- << "~[" << this->GetOutput()->GetBinMinFromValue( 1, run[1] )
- << "," << this->GetOutput()->GetBinMaxFromValue( 1, run[1] ) << "]"
- << std::endl );
}
}
}
}
template<typename TImageType, typename THistogramFrequencyContainer>
void
EnhancedScalarImageToRunLengthMatrixFilter<TImageType, THistogramFrequencyContainer>
::SetPixelValueMinMax( PixelType min, PixelType max )
{
if( this->m_Min != min || this->m_Max != max )
{
itkDebugMacro( "setting Min to " << min << "and Max to " << max );
this->m_Min = min;
this->m_Max = max;
this->Modified();
}
}
template<typename TImageType, typename THistogramFrequencyContainer>
void
EnhancedScalarImageToRunLengthMatrixFilter<TImageType, THistogramFrequencyContainer>
::SetDistanceValueMinMax( RealType min, RealType max )
{
if( this->m_MinDistance != min || this->m_MaxDistance != max )
{
itkDebugMacro( "setting MinDistance to " << min << "and MaxDistance to "
<< max );
this->m_MinDistance = min;
this->m_MaxDistance = max;
this->Modified();
}
}
template<typename TImageType, typename THistogramFrequencyContainer>
void
EnhancedScalarImageToRunLengthMatrixFilter<TImageType, THistogramFrequencyContainer>
::PrintSelf( std::ostream& os, Indent indent ) const
{
Superclass::PrintSelf( os,indent );
os << indent << "Offsets: " << this->GetOffsets() << std::endl;
os << indent << "Min: " << this->m_Min << std::endl;
os << indent << "Max: " << this->m_Max << std::endl;
os << indent << "Min distance: " << this->m_MinDistance << std::endl;
os << indent << "Max distance: " << this->m_MaxDistance << std::endl;
os << indent << "NumberOfBinsPerAxis: " << this->m_NumberOfBinsPerAxis
<< std::endl;
os << indent << "InsidePixelValue: " << this->m_InsidePixelValue << std::endl;
}
template<typename TImageType, typename THistogramFrequencyContainer>
void
EnhancedScalarImageToRunLengthMatrixFilter<TImageType, THistogramFrequencyContainer>
::NormalizeOffsetDirection(OffsetType &offset)
{
itkDebugMacro("old offset = " << offset << std::endl);
int sign = 1;
bool metLastNonZero = false;
for (int i = offset.GetOffsetDimension()-1; i>=0; i--)
{
if (metLastNonZero)
{
offset[i] *= sign;
}
else if (offset[i] != 0)
{
sign = (offset[i] > 0 ) ? 1 : -1;
metLastNonZero = true;
offset[i] *= sign;
}
}
itkDebugMacro("new offset = " << offset << std::endl);
}
} // end of namespace Statistics
} // end of namespace itk
#endif
diff --git a/Modules/Classification/CLUtilities/include/itkLocalIntensityFilter.hxx b/Modules/Classification/CLUtilities/include/itkLocalIntensityFilter.hxx
index a5f14bd8c3..ac6a61b5b6 100644
--- a/Modules/Classification/CLUtilities/include/itkLocalIntensityFilter.hxx
+++ b/Modules/Classification/CLUtilities/include/itkLocalIntensityFilter.hxx
@@ -1,314 +1,316 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef itkLocalIntensityFilter_cpp
#define itkLocalIntensityFilter_cpp
#include <itkLocalIntensityFilter.h>
#include <itkNeighborhoodIterator.h>
#include <itkImageRegionIterator.h>
#include <itkImageIterator.h>
#include <limits>
#include "itkImageScanlineIterator.h"
#include "itkProgressReporter.h"
namespace itk
{
template< typename TInputImage >
LocalIntensityFilter< TInputImage >
::LocalIntensityFilter() :m_ThreadLocalMaximum(1), m_ThreadLocalPeakValue(1), m_ThreadGlobalPeakValue(1)
{
+ this->DynamicMultiThreadingOff();
+
// first output is a copy of the image, DataObject created by
// superclass
// allocate the data objects for the outputs which are
// just decorators around real types
for (int i = 1; i < 4; ++i)
{
typename RealObjectType::Pointer output =
static_cast< RealObjectType * >(this->MakeOutput(i).GetPointer());
this->ProcessObject::SetNthOutput(i, output.GetPointer());
}
}
template< typename TInputImage >
DataObject::Pointer
LocalIntensityFilter< TInputImage >
::MakeOutput(DataObjectPointerArraySizeType output)
{
switch (output)
{
case 0:
return TInputImage::New().GetPointer();
break;
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
return RealObjectType::New().GetPointer();
break;
default:
// might as well make an image
return TInputImage::New().GetPointer();
break;
}
}
template< typename TInputImage >
typename LocalIntensityFilter< TInputImage >::RealObjectType *
LocalIntensityFilter< TInputImage >
::GetLocalPeakOutput()
{
return static_cast< RealObjectType * >(this->ProcessObject::GetOutput(1));
}
template< typename TInputImage >
const typename LocalIntensityFilter< TInputImage >::RealObjectType *
LocalIntensityFilter< TInputImage >
::GetLocalPeakOutput() const
{
return static_cast< const RealObjectType * >(this->ProcessObject::GetOutput(1));
}
template< typename TInputImage >
typename LocalIntensityFilter< TInputImage >::RealObjectType *
LocalIntensityFilter< TInputImage >
::GetGlobalPeakOutput()
{
return static_cast< RealObjectType * >(this->ProcessObject::GetOutput(2));
}
template< typename TInputImage >
const typename LocalIntensityFilter< TInputImage >::RealObjectType *
LocalIntensityFilter< TInputImage >
::GetGlobalPeakOutput() const
{
return static_cast< const RealObjectType * >(this->ProcessObject::GetOutput(2));
}
template< typename TInputImage >
typename LocalIntensityFilter< TInputImage >::RealObjectType *
LocalIntensityFilter< TInputImage >
::GetLocalMaximumOutput()
{
return static_cast< RealObjectType * >(this->ProcessObject::GetOutput(3));
}
template< typename TInputImage >
const typename LocalIntensityFilter< TInputImage >::RealObjectType *
LocalIntensityFilter< TInputImage >
::GetLocalMaximumOutput() const
{
return static_cast< const RealObjectType * >(this->ProcessObject::GetOutput(3));
}
template< typename TInputImage >
void
LocalIntensityFilter< TInputImage >
::GenerateInputRequestedRegion()
{
Superclass::GenerateInputRequestedRegion();
if (this->GetInput())
{
InputImagePointer image =
const_cast< typename Superclass::InputImageType * >(this->GetInput());
image->SetRequestedRegionToLargestPossibleRegion();
}
}
template< typename TInputImage >
void
LocalIntensityFilter< TInputImage >
::EnlargeOutputRequestedRegion(DataObject *data)
{
Superclass::EnlargeOutputRequestedRegion(data);
data->SetRequestedRegionToLargestPossibleRegion();
}
template< typename TInputImage >
void
LocalIntensityFilter< TInputImage >
::AllocateOutputs()
{
// Pass the input through as the output
InputImagePointer image =
const_cast< TInputImage * >(this->GetInput());
this->GraftOutput(image);
// Nothing that needs to be allocated for the remaining outputs
}
template< typename TInputImage >
void
LocalIntensityFilter< TInputImage >
::BeforeThreadedGenerateData()
{
- ThreadIdType numberOfThreads = this->GetNumberOfThreads();
+ ThreadIdType numberOfThreads = this->GetNumberOfWorkUnits();
// Resize the thread temporaries
m_ThreadLocalMaximum.SetSize(numberOfThreads);
m_ThreadLocalPeakValue.SetSize(numberOfThreads);
m_ThreadGlobalPeakValue.SetSize(numberOfThreads);
// Initialize the temporaries
m_ThreadLocalMaximum.Fill(std::numeric_limits< RealType>::lowest());
m_ThreadLocalPeakValue.Fill(std::numeric_limits< RealType>::lowest());
m_ThreadGlobalPeakValue.Fill(std::numeric_limits< RealType>::lowest());
}
template< typename TInputImage >
void
LocalIntensityFilter< TInputImage >
::AfterThreadedGenerateData()
{
ThreadIdType i;
- ThreadIdType numberOfThreads = this->GetNumberOfThreads();
+ ThreadIdType numberOfThreads = this->GetNumberOfWorkUnits();
RealType localMaximum = std::numeric_limits< RealType >::lowest();
RealType localPeakValue = std::numeric_limits< RealType >::lowest();
RealType globalPeakValue = std::numeric_limits< RealType >::lowest();
for (i = 0; i < numberOfThreads; i++)
{
globalPeakValue = std::max<RealType>(globalPeakValue, m_ThreadGlobalPeakValue[i]);
if (localMaximum == m_ThreadLocalMaximum[i])
{
localPeakValue = std::max< RealType >(m_ThreadLocalPeakValue[i], localPeakValue);
}
else if (localMaximum < m_ThreadLocalMaximum[i]) {
localMaximum = m_ThreadLocalMaximum[i];
localPeakValue = m_ThreadLocalPeakValue[i];
}
}
// Set the outputs
this->GetLocalPeakOutput()->Set(localPeakValue);
this->GetGlobalPeakOutput()->Set(globalPeakValue);
this->GetLocalMaximumOutput()->Set(localMaximum);
}
template< typename TInputImage >
void
LocalIntensityFilter< TInputImage >
::ThreadedGenerateData(const RegionType & outputRegionForThread,
ThreadIdType threadId)
{
typename TInputImage::ConstPointer itkImage = this->GetInput();
typename MaskImageType::Pointer itkMask = m_Mask;
double range = m_Range;
double minimumSpacing = 1;
typename TInputImage::SizeType regionSize;
int offset = std::ceil(range / minimumSpacing);
regionSize.Fill(1);
for (unsigned int i = 0; i < TInputImage::ImageDimension; ++i)
{
minimumSpacing = itkImage->GetSpacing()[i];
offset = std::ceil(range / minimumSpacing);
regionSize[i] = offset;
}
itk::ConstNeighborhoodIterator<TInputImage> iter(regionSize, itkImage, outputRegionForThread);
itk::ConstNeighborhoodIterator<MaskImageType> iterMask(regionSize, itkMask, outputRegionForThread);
typename TInputImage::PointType origin;
typename TInputImage::PointType localPoint;
itk::Index<TInputImage::ImageDimension> index;
double tmpPeakValue;
double globalPeakValue = std::numeric_limits<double>::lowest();
double localPeakValue = std::numeric_limits<double>::lowest();
PixelType localMaximum = std::numeric_limits<PixelType>::lowest();
std::vector<bool> vectorIsInRange;
index = iter.GetIndex();
itkImage->TransformIndexToPhysicalPoint(index, origin);
for (itk::SizeValueType i = 0; i < iter.Size(); ++i)
{
itkImage->TransformIndexToPhysicalPoint(iter.GetIndex(i), localPoint);
double dist = origin.EuclideanDistanceTo(localPoint);
vectorIsInRange.push_back((dist < range));
}
int count = 0;
iter.NeedToUseBoundaryConditionOff();
iterMask.NeedToUseBoundaryConditionOff();
auto imageSize = itkImage->GetLargestPossibleRegion().GetSize();
unsigned int imageDimension = itkImage->GetImageDimension();
while (!iter.IsAtEnd())
{
if (iterMask.GetCenterPixel() > 0)
{
tmpPeakValue = 0;
count = 0;
for (itk::SizeValueType i = 0; i < iter.Size(); ++i)
{
if (vectorIsInRange[i])
{
auto localIndex = iter.GetIndex(i);
bool calculatePoint = true;
for (unsigned int dimension = 0; dimension < imageDimension; ++dimension)
{
calculatePoint &= (localIndex[dimension] < static_cast<signed int>(imageSize[dimension]));
calculatePoint &= (0 <= localIndex[dimension]);
}
if (calculatePoint)
{
tmpPeakValue += iter.GetPixel(i);
++count;
}
}
}
tmpPeakValue /= count;
globalPeakValue = std::max<double>(tmpPeakValue, globalPeakValue);
auto currentCenterPixelValue = iter.GetCenterPixel();
if (localMaximum == currentCenterPixelValue)
{
localPeakValue = std::max<double>(tmpPeakValue, localPeakValue);
}
else if (localMaximum < currentCenterPixelValue)
{
localMaximum = currentCenterPixelValue;
localPeakValue = tmpPeakValue;
}
}
++iterMask;
++iter;
}
m_ThreadLocalMaximum[threadId] = localMaximum;
m_ThreadLocalPeakValue[threadId] = localPeakValue;
m_ThreadGlobalPeakValue[threadId] = globalPeakValue;
}
template< typename TImage >
void
LocalIntensityFilter< TImage >
::PrintSelf(std::ostream & os, Indent indent) const
{
Superclass::PrintSelf(os, indent);
os << indent << "Local Peak: " << this->GetLocalPeak() << std::endl;
os << indent << "Global Peak: " << this->GetGlobalPeak() << std::endl;
os << indent << "Local Maximum: " << this->GetLocalMaximum() << std::endl;
}
} // end namespace itk
#endif
diff --git a/Modules/Classification/CLUtilities/include/itkLocalStatisticFilter.hxx b/Modules/Classification/CLUtilities/include/itkLocalStatisticFilter.hxx
index a8fabc92fe..d6f18f07e5 100644
--- a/Modules/Classification/CLUtilities/include/itkLocalStatisticFilter.hxx
+++ b/Modules/Classification/CLUtilities/include/itkLocalStatisticFilter.hxx
@@ -1,113 +1,114 @@
#ifndef itkLocalStatisticFilter_cpp
#define itkLocalStatisticFilter_cpp
#include <itkLocalStatisticFilter.h>
#include <itkNeighborhoodIterator.h>
#include <itkImageRegionIterator.h>
#include <itkImageIterator.h>
#include "itkMinimumMaximumImageCalculator.h"
#include <limits>
template< class TInputImageType, class TOuputImageType>
itk::LocalStatisticFilter<TInputImageType, TOuputImageType>::LocalStatisticFilter():
m_Size(5), m_Bins(5)
{
+ this->DynamicMultiThreadingOff();
this->SetNumberOfRequiredOutputs(m_Bins);
this->SetNumberOfRequiredInputs(0);
for (int i = 0; i < m_Bins; ++i)
{
this->SetNthOutput( i, this->MakeOutput(i) );
}
}
template< class TInputImageType, class TOuputImageType>
void
itk::LocalStatisticFilter<TInputImageType, TOuputImageType>::BeforeThreadedGenerateData()
{
InputImagePointer input = this->GetInput(0);
for (int i = 0; i < m_Bins; ++i)
{
CreateOutputImage(input, this->GetOutput(i));
}
}
template< class TInputImageType, class TOuputImageType>
void
itk::LocalStatisticFilter<TInputImageType, TOuputImageType>::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType /*threadId*/)
{
typedef itk::ImageRegionIterator<TInputImageType> IteratorType;
typedef itk::ConstNeighborhoodIterator<TInputImageType> ConstIteratorType;
typename TInputImageType::SizeType size; size.Fill(m_Size);
InputImagePointer input = this->GetInput(0);
if (TInputImageType::ImageDimension == 3)
{
size[2] = 0;
}
// MITK_INFO << "Creating output iterator";
std::vector<IteratorType> iterVector;
for (int i = 0; i < m_Bins; ++i)
{
IteratorType iter(this->GetOutput(i), outputRegionForThread);
iterVector.push_back(iter);
}
ConstIteratorType inputIter(size, input, outputRegionForThread);
while (!inputIter.IsAtEnd())
{
for (int i = 0; i < m_Bins; ++i)
{
iterVector[i].Set(0);
}
double min = std::numeric_limits<double>::max();
double max = std::numeric_limits<double>::lowest();
double mean = 0;
double std = 0;
for (unsigned int i = 0; i < inputIter.Size(); ++i)
{
double value = inputIter.GetPixel(i);
min = std::min<double>(min, value);
max = std::max<double>(max, value);
mean += value / inputIter.Size();
std += (value*value) / inputIter.Size();
}
iterVector[0].Value() = min;
iterVector[1].Value() = max;
iterVector[2].Value() = mean;
iterVector[3].Value() = std::sqrt(std - mean*mean);
iterVector[4].Value() = max-min;
for (int i = 0; i < m_Bins; ++i)
{
++(iterVector[i]);
}
++inputIter;
}
}
template< class TInputImageType, class TOuputImageType>
itk::ProcessObject::DataObjectPointer
itk::LocalStatisticFilter<TInputImageType, TOuputImageType>::MakeOutput(itk::ProcessObject::DataObjectPointerArraySizeType /*idx*/)
{
itk::ProcessObject::DataObjectPointer output;
output = ( TOuputImageType::New() ).GetPointer();
return output;
}
template< class TInputImageType, class TOuputImageType>
void
itk::LocalStatisticFilter<TInputImageType, TOuputImageType>::CreateOutputImage(InputImagePointer input, OutputImagePointer output)
{
output->SetRegions(input->GetLargestPossibleRegion());
output->Allocate();
}
#endif //itkLocalStatisticFilter_cpp
diff --git a/Modules/Classification/CLUtilities/include/itkMultiHistogramFilter.cpp b/Modules/Classification/CLUtilities/include/itkMultiHistogramFilter.cpp
index b18a65904c..2eabf31065 100644
--- a/Modules/Classification/CLUtilities/include/itkMultiHistogramFilter.cpp
+++ b/Modules/Classification/CLUtilities/include/itkMultiHistogramFilter.cpp
@@ -1,124 +1,125 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef itkMultiHistogramFilter_cpp
#define itkMultiHistogramFilter_cpp
#include <itkMultiHistogramFilter.h>
#include <itkNeighborhoodIterator.h>
#include <itkImageRegionIterator.h>
#include <itkImageIterator.h>
#include "itkMinimumMaximumImageCalculator.h"
template< class TInputImageType, class TOuputImageType>
itk::MultiHistogramFilter<TInputImageType, TOuputImageType>::MultiHistogramFilter():
m_Delta(0.6), m_Offset(-3.0), m_Bins(11), m_Size(5), m_UseImageIntensityRange(false)
{
+ this->DynamicMultiThreadingOff();
this->SetNumberOfRequiredOutputs(m_Bins);
this->SetNumberOfRequiredInputs(0);
for (int i = 0; i < m_Bins; ++i)
{
this->SetNthOutput( i, this->MakeOutput(i) );
}
}
template< class TInputImageType, class TOuputImageType>
void
itk::MultiHistogramFilter<TInputImageType, TOuputImageType>::BeforeThreadedGenerateData()
{
typedef itk::MinimumMaximumImageCalculator <TInputImageType>
ImageCalculatorFilterType;
if (m_UseImageIntensityRange)
{
typename ImageCalculatorFilterType::Pointer imageCalculatorFilter
= ImageCalculatorFilterType::New();
imageCalculatorFilter->SetImage(this->GetInput(0));
imageCalculatorFilter->Compute();
m_Offset = imageCalculatorFilter->GetMinimum();
m_Delta = 1.0*(imageCalculatorFilter->GetMaximum() - imageCalculatorFilter->GetMinimum()) / (1.0*m_Bins);
}
InputImagePointer input = this->GetInput(0);
for (int i = 0; i < m_Bins; ++i)
{
CreateOutputImage(input, this->GetOutput(i));
}
}
template< class TInputImageType, class TOuputImageType>
void
itk::MultiHistogramFilter<TInputImageType, TOuputImageType>::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType /*threadId*/)
{
double offset = m_Offset;// -3.0;
double delta = m_Delta;// 0.6;
typedef itk::ImageRegionIterator<TInputImageType> IteratorType;
typedef itk::ConstNeighborhoodIterator<TInputImageType> ConstIteratorType;
typename TInputImageType::SizeType size; size.Fill(m_Size);
InputImagePointer input = this->GetInput(0);
// MITK_INFO << "Creating output iterator";
std::vector<IteratorType> iterVector;
for (int i = 0; i < m_Bins; ++i)
{
IteratorType iter(this->GetOutput(i), outputRegionForThread);
iterVector.push_back(iter);
}
ConstIteratorType inputIter(size, input, outputRegionForThread);
while (!inputIter.IsAtEnd())
{
for (int i = 0; i < m_Bins; ++i)
{
iterVector[i].Set(0);
}
for (unsigned int i = 0; i < inputIter.Size(); ++i)
{
double value = inputIter.GetPixel(i);
value -= offset;
value /= delta;
auto pos = (int)(value);
pos = std::max(0, std::min(m_Bins-1, pos));
iterVector[pos].Value() += 1;// (iterVector[pos].GetCenterPixel() + 1);
}
for (int i = 0; i < m_Bins; ++i)
{
++(iterVector[i]);
}
++inputIter;
}
}
template< class TInputImageType, class TOuputImageType>
itk::ProcessObject::DataObjectPointer
itk::MultiHistogramFilter<TInputImageType, TOuputImageType>::MakeOutput(itk::ProcessObject::DataObjectPointerArraySizeType /*idx*/)
{
itk::ProcessObject::DataObjectPointer output;
output = ( TOuputImageType::New() ).GetPointer();
return output;
}
template< class TInputImageType, class TOuputImageType>
void
itk::MultiHistogramFilter<TInputImageType, TOuputImageType>::CreateOutputImage(InputImagePointer input, OutputImagePointer output)
{
output->SetRegions(input->GetLargestPossibleRegion());
output->Allocate();
}
#endif //itkMultiHistogramFilter_cpp
diff --git a/Modules/Classification/CLUtilities/include/itkNeighborhoodFunctorImageFilter.h b/Modules/Classification/CLUtilities/include/itkNeighborhoodFunctorImageFilter.h
index 6942ce74c4..229a132ebd 100644
--- a/Modules/Classification/CLUtilities/include/itkNeighborhoodFunctorImageFilter.h
+++ b/Modules/Classification/CLUtilities/include/itkNeighborhoodFunctorImageFilter.h
@@ -1,152 +1,153 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef itkNeighborhoodFunctorImageFilter_h
#define itkNeighborhoodFunctorImageFilter_h
#include "itkImageToImageFilter.h"
#include "itkZeroFluxNeumannBoundaryCondition.h"
#include "itkConstNeighborhoodIterator.h"
#include "itkImage.h"
#include <cstdio>
#include <itkHistogramToTextureFeaturesFilter.h>
#include "itkHistogram.h"
namespace itk
{
template<typename TInputImageType, typename TFeatureImageType , class FunctorType>
class NeighborhoodFunctorImageFilter : public ImageToImageFilter< TInputImageType, TFeatureImageType>
{
public:
typedef NeighborhoodFunctorImageFilter Self;
typedef ImageToImageFilter<TInputImageType, TFeatureImageType> Superclass;
typedef SmartPointer< Self > Pointer;
typedef SmartPointer< const Self > ConstPointer;
itkNewMacro(Self);
itkTypeMacro(NeighborhoodFunctorImageFilter, ImageToImageFilter);
/** Extract some information from the image types. Dimensionality
* of the two images is assumed to be the same. */
itkStaticConstMacro(ImageDimension, unsigned int,
TFeatureImageType::ImageDimension);
itkStaticConstMacro(InputImageDimension, unsigned int,
TInputImageType::ImageDimension);
typedef TInputImageType InputImageType;
typedef typename TInputImageType::PixelType InputImagePixelType;
typedef itk::Image<short, InputImageDimension> MaskImageType;
typedef typename MaskImageType::PixelType MaskImagePixelType;
typedef TFeatureImageType FeatureImageType;
typedef typename FeatureImageType::PixelType FeaturePixelType;
typedef itk::Size<InputImageDimension> SizeType;
/** Typedef for generic boundary condition pointer. */
typedef ImageBoundaryCondition< InputImageType > * ImageBoundaryConditionPointerType;
/** Typedef for the default boundary condition */
typedef ZeroFluxNeumannBoundaryCondition< InputImageType > DefaultBoundaryCondition;
/** Superclass typedefs. */
typedef typename Superclass::OutputImageRegionType OutputImageRegionType;
typedef Neighborhood< InputImagePixelType, InputImageDimension > NeighborhoodType;
/** Allows a user to override the internal boundary condition. Care should be
* be taken to ensure that the overriding boundary condition is a persistent
* object during the time it is referenced. The overriding condition
* can be of a different type than the default type as long as it is
* a subclass of ImageBoundaryCondition. */
void OverrideBoundaryCondition(const ImageBoundaryConditionPointerType i)
{ m_BoundsCondition = i; }
/** Get the boundary condition specified */
ImageBoundaryConditionPointerType GetBoundaryCondition()
{ return m_BoundsCondition; }
void SetNeighborhoodSize(SizeType size){m_Size = size;}
void SetNeighborhoodSize(unsigned int size){m_Size.Fill(size);}
SizeType GetNeighborhoodSize(){return m_Size;}
void SetMask(const typename MaskImageType::Pointer & ptr){m_MaskImage = ptr;}
const FunctorType & GetFunctorReference() const
{
return m_Functor;
}
FunctorType & GetFunctorReference()
{
return m_Functor;
}
void SetFunctor(const FunctorType & func)
{
m_Functor = func;
}
protected:
NeighborhoodFunctorImageFilter()
{
+ this->DynamicMultiThreadingOff();
m_Size.Fill(0);
m_MaskImage = nullptr;
m_BoundsCondition = static_cast< ImageBoundaryConditionPointerType >( &m_DefaultBoundaryCondition );
this->SetNumberOfIndexedOutputs(FunctorType::OutputCount);
}
~NeighborhoodFunctorImageFilter() override{}
void BeforeThreadedGenerateData() override;
void ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread,
ThreadIdType threadId) override;
/** NeighborhoodFunctorImageFilter needs a larger input requested
* region than the output requested region. As such,
* NeighborhoodOperatorImageFilter needs to provide an implementation for
* GenerateInputRequestedRegion() in order to inform the pipeline
* execution model.
*
* \sa ProcessObject::GenerateInputRequestedRegion() */
void GenerateInputRequestedRegion() override;
private:
NeighborhoodFunctorImageFilter(const Self &); // purposely not implemented
void operator=(const Self &); // purposely not implemented
/** Pointer to a persistent boundary condition object used
* for the image iterator. */
ImageBoundaryConditionPointerType m_BoundsCondition;
/** Default boundary condition */
DefaultBoundaryCondition m_DefaultBoundaryCondition;
/** Internal operator used to filter the image. */
FunctorType m_Functor;
itk::Size<InputImageDimension> m_Size;
typename MaskImageType::Pointer m_MaskImage;
};
}
#ifndef ITK_MANUAL_INSTANTIATION
#include "../src/Features/itkNeighborhoodFunctorImageFilter.cpp"
#endif
#endif // itkFeatureImageFilter_h
diff --git a/Modules/Classification/CLUtilities/src/Features/itkLineHistogramBasedMassImageFilter.cpp b/Modules/Classification/CLUtilities/src/Features/itkLineHistogramBasedMassImageFilter.cpp
index ad2cb43f2c..626150db4b 100644
--- a/Modules/Classification/CLUtilities/src/Features/itkLineHistogramBasedMassImageFilter.cpp
+++ b/Modules/Classification/CLUtilities/src/Features/itkLineHistogramBasedMassImageFilter.cpp
@@ -1,254 +1,256 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef ITKLINEHISTOGRAMBASEDMASSIMAGEFILTER_CPP
#define ITKLINEHISTOGRAMBASEDMASSIMAGEFILTER_CPP
#include <itkLineHistogramBasedMassImageFilter.h>
+#include <itkImageRegionConstIterator.h>
#include <itkImageRegionIteratorWithIndex.h>
#include <itkBinaryContourImageFilter.h>
#include <mitkImageCast.h>
#include <mitkIOUtil.h>
template< class TInputImageType, class TOutputImageType, class TMaskImageType>
void itk::LineHistogramBasedMassImageFilter<TInputImageType,TOutputImageType,TMaskImageType>
::BeforeThreadedGenerateData()
{
if(m_ImageMask.IsNull())
{
itkExceptionMacro("No binary mask image provided.")
}
if(m_BinaryContour.IsNull()){
typename TMaskImageType::RegionType region = m_ImageMask->GetLargestPossibleRegion();
unsigned int xdim = region.GetSize(0);
unsigned int ydim = region.GetSize(1);
unsigned int zdim = region.GetSize(2);
// ensure border pixels are zero so that an closed contour is created
typename TMaskImageType::Pointer mask_copy = TMaskImageType::New();
mask_copy->SetSpacing(m_ImageMask->GetSpacing());
mask_copy->SetDirection(m_ImageMask->GetDirection());
mask_copy->SetOrigin(m_ImageMask->GetOrigin());
mask_copy->SetRegions(region);
mask_copy->Allocate();
mask_copy->FillBuffer(0);
itk::ImageRegionIteratorWithIndex<TMaskImageType> oit(mask_copy, mask_copy->GetLargestPossibleRegion());
itk::ImageRegionConstIteratorWithIndex<TMaskImageType> mit(m_ImageMask, m_ImageMask->GetLargestPossibleRegion());
while(!mit.IsAtEnd())
{
if(mit.Value() != 0 //is under the mask
&& mit.GetIndex()[0] != 0 //is not at the border
&& mit.GetIndex()[1] != 0
&& mit.GetIndex()[2] != 0
&& mit.GetIndex()[0] != xdim-1
&& mit.GetIndex()[1] != ydim-1
&& mit.GetIndex()[2] != zdim-1)
{
oit.Set(1);
}
++mit;
++oit;
}
typedef itk::BinaryContourImageFilter<TMaskImageType,TMaskImageType> BinaryContourImagefilterType;
typename BinaryContourImagefilterType::Pointer filter = BinaryContourImagefilterType::New();
filter->SetInput(mask_copy);
filter->SetBackgroundValue(0);
filter->SetForegroundValue(1);
filter->SetFullyConnected(true);
filter->Update();
m_BinaryContour = filter->GetOutput();
mitk::Image::Pointer outimg;
mitk::CastToMitkImage(m_BinaryContour,outimg);
}
if(m_BinaryContour.IsNull())
{
itkExceptionMacro("No binary contour image provided.")
}
m_CenterOfMask = GetCenterOfMass(m_ImageMask);
if(m_CenterOfMask.is_zero())
{
itkExceptionMacro("Center of mass is corrupt.")
}
}
template< class TInputImageType, class TOutputImageType, class TMaskImageType>
vnl_vector<double> itk::LineHistogramBasedMassImageFilter<TInputImageType,TOutputImageType,TMaskImageType>
::GetCenterOfMass(const TMaskImageType * maskImage)
{
mitk::Image::Pointer img;
mitk::CastToMitkImage(maskImage, img);
typedef itk::ImageRegionConstIterator< TMaskImageType > IteratorType;
IteratorType iter( maskImage, maskImage->GetLargestPossibleRegion() );
iter.GoToBegin();
vnl_vector_fixed<double,3> mean_index;
mean_index.fill(0);
unsigned int count = 0;
while ( !iter.IsAtEnd() )
{
if ( iter.Get() != 0 )
{
mitk::Point3D current_index_pos;
img->GetGeometry()->IndexToWorld(iter.GetIndex(),current_index_pos);
mean_index += current_index_pos.GetVnlVector();
count++;
}
++iter;
}
mean_index /= count;
- return mean_index;
+ return mean_index.as_ref();
}
template< class TInputImageType, class TOutputImageType, class TMaskImageType>
void itk::LineHistogramBasedMassImageFilter<TInputImageType,TOutputImageType,TMaskImageType>
::ThreadedGenerateData(const typename Superclass::OutputImageRegionType &outputRegionForThread, ThreadIdType /*threadId*/)
{
TOutputImageType * outimage = this->GetOutput();
itk::ImageRegionConstIteratorWithIndex<TMaskImageType> cit(m_BinaryContour, outputRegionForThread);
itk::ImageRegionConstIteratorWithIndex<TInputImageType> iit(this->GetInput(), outputRegionForThread);
itk::ImageRegionIteratorWithIndex<TOutputImageType > oit(outimage,outputRegionForThread);
typedef typename itk::ImageRegionIteratorWithIndex<TInputImageType >::IndexType IndexType;
std::vector<IndexType> target_world_indices;
while(!cit.IsAtEnd())
{
if(cit.Value() != 0 )
target_world_indices.push_back(cit.GetIndex());
++cit;
}
mitk::Image::Pointer image;
mitk::CastToMitkImage(this->GetInput(), image);
mitk::BaseGeometry * transform = image->GetGeometry();
while(!target_world_indices.empty())
{
vnl_vector<double> u_v(3,1), x_v(3,1), p_v(3,1);
double line_histo_area = 0;
// w->i skull point
mitk::Point3D skull_point, tmp_point;
image->GetGeometry()->IndexToWorld(target_world_indices.back(),skull_point);
// project centerpoint to slice
// x = p + s*u
u_v = skull_point.GetVnlVector() - m_CenterOfMask;
u_v.normalize();
p_v = m_CenterOfMask;
//step width
double s_s = 0.1;
// i->w center point
mitk::Point3D center_point;
center_point[0] = m_CenterOfMask[0];
center_point[1] = m_CenterOfMask[1];
center_point[2] = m_CenterOfMask[2];
IndexType tmp_index;
transform->WorldToIndex(center_point,tmp_index);
oit.SetIndex(tmp_index);
std::vector<IndexType> under_line_indices;
while(true)
{
// store current_index
under_line_indices.push_back(tmp_index);
// set histo value
iit.SetIndex(tmp_index);
line_histo_area += iit.Value();
// break in end reached
if(tmp_index == target_world_indices.back())
{
break;
}
// get next voxel index under the line
while(tmp_index == oit.GetIndex()) // if new voxel index reached brake
{
x_v = p_v + s_s * u_v; // next
tmp_point.GetVnlVector().set(x_v.data_block());
transform->WorldToIndex(tmp_point, tmp_index);
s_s+=0.1;
}
oit.SetIndex(tmp_index);
}
while (!under_line_indices.empty()) {
IndexType current_index = under_line_indices.back();
under_line_indices.pop_back();
oit.SetIndex(current_index);
oit.Set(line_histo_area / (s_s * u_v).magnitude() );
}
target_world_indices.pop_back();
}
}
template< class TInputImageType, class TOutputImageType, class TMaskImageType>
void itk::LineHistogramBasedMassImageFilter<TInputImageType,TOutputImageType,TMaskImageType>::SetImageMask(TMaskImageType * maskimage)
{
this->m_ImageMask = maskimage;
}
template< class TInputImageType, class TOutputImageType, class TMaskImageType>
void itk::LineHistogramBasedMassImageFilter<TInputImageType,TOutputImageType,TMaskImageType>::SetBinaryContour(TMaskImageType * binarycontour)
{
this->m_BinaryContour = binarycontour;
}
template< class TInputImageType, class TOutputImageType, class TMaskImageType>
itk::LineHistogramBasedMassImageFilter<TInputImageType,TOutputImageType,TMaskImageType>::LineHistogramBasedMassImageFilter()
{
+ this->DynamicMultiThreadingOff();
this->SetNumberOfIndexedOutputs(1);
this->SetNumberOfIndexedInputs(1);
m_CenterOfMask.fill(0);
}
template< class TInputImageType, class TOutputImageType, class TMaskImageType>
itk::LineHistogramBasedMassImageFilter<TInputImageType,TOutputImageType,TMaskImageType>::~LineHistogramBasedMassImageFilter()
{
}
#endif
diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCurvatureStatistic.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCurvatureStatistic.cpp
index 5bd6a01c32..029c3329a2 100644
--- a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCurvatureStatistic.cpp
+++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCurvatureStatistic.cpp
@@ -1,170 +1,164 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <mitkGIFCurvatureStatistic.h>
// MITK
#include <mitkITKImageImport.h>
#include <mitkImageCast.h>
#include <mitkImageAccessByItk.h>
#include <mitkPixelTypeMultiplex.h>
#include <mitkImagePixelReadAccessor.h>
// ITK
#include <itkLabelStatisticsImageFilter.h>
#include <itkNeighborhoodIterator.h>
// VTK
#include <vtkSmartPointer.h>
#include <vtkImageMarchingCubes.h>
#include <vtkCurvatures.h>
#include <vtkPointData.h>
// STL
#include <sstream>
#include <limits>
static void calculateLocalStatistic(vtkDataArray* scalars, const std::string& name, const mitk::FeatureID& featureID, mitk::GIFCurvatureStatistic::FeatureListType & featureList)
{
int size = scalars->GetNumberOfTuples();
double minimum = std::numeric_limits<double>::max();
double maximum = std::numeric_limits<double>::lowest();
double mean1 = 0;
double mean2 = 0;
double mean3 = 0;
- double mean4 = 0;
double mean1p = 0;
double mean2p = 0;
double mean3p = 0;
- double mean4p = 0;
double mean1n = 0;
double mean2n = 0;
double mean3n = 0;
- double mean4n = 0;
int countPositive = 0;
int countNegative = 0;
for (int i = 0; i < size; ++i)
{
double actualValue = scalars->GetComponent(i, 0);
minimum = std::min<double>(minimum, scalars->GetComponent(i, 0));
maximum = std::max<double>(maximum, scalars->GetComponent(i, 0));
mean1 += actualValue;
mean2 += actualValue*actualValue;
mean3 += actualValue*actualValue*actualValue;
- mean4 += actualValue*actualValue*actualValue*actualValue;
if (actualValue > 0)
{
mean1p += actualValue;
mean2p += actualValue*actualValue;
mean3p += actualValue*actualValue*actualValue;
- mean4p += actualValue*actualValue*actualValue*actualValue;
countPositive++;
}
if (actualValue < 0)
{
mean1n += actualValue;
mean2n += actualValue*actualValue;
mean3n += actualValue*actualValue*actualValue;
- mean4n += actualValue*actualValue*actualValue*actualValue;
countNegative++;
}
}
double mean = mean1 / size;
double stddev = std::sqrt(mean2 / size - mean*mean);
double skewness = ((mean3 / size) - 3 * mean*stddev*stddev - mean*mean*mean) / (stddev*stddev*stddev);
double meanP = mean1p / countPositive;
double stddevP = std::sqrt(mean2p / countPositive - meanP*meanP);
double skewnessP = ((mean3p / countPositive) - 3 * meanP*stddevP*stddevP - meanP*meanP*meanP) / (stddevP*stddevP*stddevP);
double meanN = mean1n / countNegative;
double stddevN = std::sqrt(mean2n / countNegative - meanN*meanN);
double skewnessN = ((mean3n / countNegative) - 3 * meanN*stddevN*stddevN - meanN*meanN*meanN) / (stddevN*stddevN*stddevN);
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Minimum " + name + " Curvature"), minimum));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Maximum " + name + " Curvature"), maximum));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Mean " + name + " Curvature"), mean));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Standard Deviation " + name + " Curvature"), stddev));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Skewness " + name + " Curvature"), skewness));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Mean Positive " + name + " Curvature"), meanP));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Standard Deviation Positive " + name + " Curvature"), stddevP));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Skewness Positive " + name + " Curvature"), skewnessP));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Mean Negative " + name + " Curvature"), meanN));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Standard Deviation Negative " + name + " Curvature"), stddevN));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Skewness Negative " + name + " Curvature"), skewnessN));
}
mitk::GIFCurvatureStatistic::GIFCurvatureStatistic()
{
SetLongName("curvature");
SetShortName("cur");
SetFeatureClassName("Curvature Feature");
}
void mitk::GIFCurvatureStatistic::AddArguments(mitkCommandLineParser &parser) const
{
std::string name = GetOptionPrefix();
parser.addArgument(GetLongName(), name, mitkCommandLineParser::Bool, "Use Curvature of Surface as feature", "calculates shape curvature based features", us::Any());
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFCurvatureStatistic::DoCalculateFeatures(const Image* image, const Image* mask)
{
FeatureListType featureList;
if (image->GetDimension() < 3)
{
MITK_INFO << "Passed calculating volumetric features due to wrong dimensionality ....";
}
else
{
MITK_INFO << "Start calculating volumetric features ....";
auto id = this->CreateTemplateFeatureID();
vtkSmartPointer<vtkImageMarchingCubes> mesher = vtkSmartPointer<vtkImageMarchingCubes>::New();
vtkSmartPointer<vtkCurvatures> curvator = vtkSmartPointer<vtkCurvatures>::New();
auto nonconstVtkData = const_cast<vtkImageData*>(mask->GetVtkImageData());
mesher->SetInputData(nonconstVtkData);
mesher->SetValue(0, 0.5);
curvator->SetInputConnection(mesher->GetOutputPort());
curvator->SetCurvatureTypeToMean();
curvator->Update();
vtkDataArray* scalars = curvator->GetOutput()->GetPointData()->GetScalars();
calculateLocalStatistic(scalars, "Mean", id, featureList);
curvator->SetCurvatureTypeToGaussian();
curvator->Update();
scalars = curvator->GetOutput()->GetPointData()->GetScalars();
calculateLocalStatistic(scalars, "Gaussian", id, featureList);
curvator->SetCurvatureTypeToMinimum();
curvator->Update();
scalars = curvator->GetOutput()->GetPointData()->GetScalars();
calculateLocalStatistic(scalars, "Minimum", id, featureList);
curvator->SetCurvatureTypeToMaximum();
curvator->Update();
scalars = curvator->GetOutput()->GetPointData()->GetScalars();
calculateLocalStatistic(scalars, "Maximum", id, featureList);
MITK_INFO << "Finished calculating volumetric features....";
}
return featureList;
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFCurvatureStatistic::CalculateFeatures(const Image* image, const Image* mask, const Image*)
{
return Superclass::CalculateFeatures(image, mask);
}
diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderNumericStatistics.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderNumericStatistics.cpp
index a1d4df5296..5f9f1d43a8 100644
--- a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderNumericStatistics.cpp
+++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderNumericStatistics.cpp
@@ -1,339 +1,335 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <mitkGIFFirstOrderNumericStatistics.h>
// MITK
#include <mitkITKImageImport.h>
#include <mitkImageCast.h>
#include <mitkImageAccessByItk.h>
// ITK
#include <itkLabelStatisticsImageFilter.h>
#include <itkMinimumMaximumImageCalculator.h>
#include <itkImageRegionIterator.h>
// STL
#include <sstream>
struct FirstOrderNumericParameterStruct {
mitk::IntensityQuantifier::Pointer quantifier;
double MinimumIntensity;
double MaximumIntensity;
int Bins;
mitk::FeatureID id;
};
template<typename TPixel, unsigned int VImageDimension>
void
CalculateFirstOrderStatistics(const itk::Image<TPixel, VImageDimension>* itkImage, const mitk::Image* mask, mitk::GIFFirstOrderNumericStatistics::FeatureListType & featureList, FirstOrderNumericParameterStruct params)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::Image<unsigned short, VImageDimension> MaskType;
typename MaskType::Pointer maskImage = MaskType::New();
mitk::CastToItkImage(mask, maskImage);
//
// Calculate the Volume of Voxel (Maximum to the 3th order)
//
double voxelVolume = 1;
for (unsigned int i = 0; i < std::min<unsigned int>(3, VImageDimension); ++i)
voxelVolume *= itkImage->GetSpacing()[i];
//
// Calculate the Hypervolume of Voxel
//
double voxelSpace = 1;
for (unsigned int i = 0; i < VImageDimension; ++i)
voxelSpace *= itkImage->GetSpacing()[i];
unsigned int numberOfBins = params.quantifier->GetBins();
std::vector<unsigned int> histogram;
histogram.resize(numberOfBins, 0);
double minimum = std::numeric_limits<double>::max();
double maximum = std::numeric_limits<double>::lowest();
double absoluteMinimum = std::numeric_limits<double>::max();
double absoluteMaximum = std::numeric_limits<double>::lowest();
double sum = 0;
double sumTwo= 0;
- double sumThree = 0;
unsigned int numberOfVoxels = 0;
itk::ImageRegionConstIterator<ImageType> imageIter(itkImage, itkImage->GetLargestPossibleRegion());
itk::ImageRegionConstIterator<MaskType> maskIter(maskImage, maskImage->GetLargestPossibleRegion());
while (!imageIter.IsAtEnd())
{
double value = imageIter.Get();
absoluteMinimum = std::min<double>(minimum, value);
absoluteMaximum = std::max<double>(maximum, value);
if (maskIter.Get() > 0)
{
minimum = std::min<double>(minimum, value);
maximum = std::max<double>(maximum, value);
sum += value;
sumTwo += value * value;
- sumThree += value * value*value;
histogram[params.quantifier->IntensityToIndex(value)] += 1;
++numberOfVoxels;
}
++maskIter;
++imageIter;
}
//
// Histogram based calculations
//
unsigned int passedValues = 0;
double doubleNoOfVoxes = numberOfVoxels;
double median = 0;
double lastIntensityWithValues = params.quantifier->IndexToMeanIntensity(0);
std::size_t modeIdx = 0;
double entropy = 0;
double uniformity = 0;
std::vector<double> percentiles;
percentiles.resize(20, 0);
for (std::size_t idx = 0; idx < histogram.size(); ++idx)
{
unsigned int actualValues = histogram[idx];
for (std::size_t percentileIdx = 0; percentileIdx < percentiles.size(); ++percentileIdx)
{
double threshold = doubleNoOfVoxes * (percentileIdx + 1) *1.0 / (percentiles.size());
if ((passedValues < threshold) & ((passedValues + actualValues) >= threshold))
{
// Lower Bound
if (passedValues == std::floor(threshold))
{
percentiles[percentileIdx] = 0.5*(lastIntensityWithValues + params.quantifier->IndexToMeanIntensity(idx));
}
else
{
percentiles[percentileIdx] = params.quantifier->IndexToMeanIntensity(idx);
}
}
}
if ((passedValues < doubleNoOfVoxes * 0.5) & ((passedValues + actualValues) >= doubleNoOfVoxes * 0.5))
{
// Lower Bound
if (passedValues == std::floor(doubleNoOfVoxes * 0.5))
{
median = 0.5*(lastIntensityWithValues + params.quantifier->IndexToMeanIntensity(idx));
}
else
{
median = params.quantifier->IndexToMeanIntensity(idx);
}
}
if (actualValues > histogram[modeIdx])
{
modeIdx = idx;
}
if (actualValues > 0)
{
lastIntensityWithValues = params.quantifier->IndexToMeanIntensity(idx);
double currentProbability = actualValues / (1.0 *numberOfVoxels);
uniformity += currentProbability * currentProbability;
entropy += currentProbability * std::log(currentProbability) / std::log(2);
}
passedValues += actualValues;
}
double p10 = percentiles[1];
//double p25idx = params.quantifier->IntensityToIndex(percentiles[4]);
//double p75idx = params.quantifier->IntensityToIndex(percentiles[14]);
double p25idx = percentiles[4];
double p75idx = percentiles[14];
double p90 = percentiles[17];
double mean = sum / (numberOfVoxels);
double variance = sumTwo / (numberOfVoxels) - (mean*mean);
double energy = sumTwo;
double rootMeanSquare = std::sqrt(sumTwo / numberOfVoxels);
double sumAbsoluteDistanceToMean = 0;
double sumAbsoluteDistanceToMedian = 0;
double sumRobust = 0;
double sumRobustSquare = 0;
double sumRobustAbsolulteDistanceToMean = 0;
- double sumValueMinusMean = 0;
double sumValueMinusMeanThree = 0;
double sumValueMinusMeanFour = 0;
unsigned int numberOfRobustVoxel = 0;
maskIter.GoToBegin();
imageIter.GoToBegin();
while (!imageIter.IsAtEnd())
{
if (maskIter.Get() > 0)
{
double value = imageIter.Get();
double valueMinusMean = value - mean;
sumAbsoluteDistanceToMean += std::abs<double>(valueMinusMean);
sumAbsoluteDistanceToMedian += std::abs<double>(value - median);
- sumValueMinusMean += valueMinusMean;
sumValueMinusMeanThree += valueMinusMean * valueMinusMean * valueMinusMean;
sumValueMinusMeanFour += valueMinusMean * valueMinusMean * valueMinusMean * valueMinusMean;
if ((p10 <= value) & (value <= p90))
{
sumRobust += value;
sumRobustSquare += value * value;
++numberOfRobustVoxel;
}
}
++maskIter;
++imageIter;
}
double robustMean = sumRobust / numberOfRobustVoxel;
double robustVariance = sumRobustSquare / numberOfRobustVoxel - (robustMean * robustMean);
maskIter.GoToBegin();
imageIter.GoToBegin();
while (!imageIter.IsAtEnd())
{
if (maskIter.Get() > 0)
{
double value = imageIter.Get();
if ((p10 <= value) & (value <= p90))
{
sumRobustAbsolulteDistanceToMean += std::abs(value - robustMean);
}
}
++maskIter;
++imageIter;
}
double meanAbsoluteDeviation = sumAbsoluteDistanceToMean / numberOfVoxels;
double medianAbsoluteDeviation = sumAbsoluteDistanceToMedian / numberOfVoxels;
double robustMeanAbsoluteDeviation = sumRobustAbsolulteDistanceToMean / numberOfRobustVoxel;
double skewness = sumValueMinusMeanThree / numberOfVoxels / variance / std::sqrt(variance);
double kurtosis = sumValueMinusMeanFour / numberOfVoxels / variance / variance;
double interquantileRange = p75idx - p25idx;
double coefficientOfVariation = std::sqrt(variance) / mean;
double quantileCoefficientOfDispersion = (p75idx - p25idx) / (p75idx + p25idx);
double coveredImageRange = (maximum - minimum)/ (absoluteMaximum - absoluteMinimum) ;
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Mean"),mean));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Variance"),variance));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Skewness"),skewness));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Excess kurtosis"),kurtosis-3));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Median"),median));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Minimum"),minimum));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "05th Percentile"),percentiles[0]));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "10th Percentile"),percentiles[1]));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "15th Percentile"),percentiles[2]));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "20th Percentile"),percentiles[3]));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "25th Percentile"),percentiles[4]));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "30th Percentile"),percentiles[5]));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "35th Percentile"),percentiles[6]));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "40th Percentile"),percentiles[7]));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "45th Percentile"),percentiles[8]));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "50th Percentile"),percentiles[9]));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "55th Percentile"),percentiles[10]));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "60th Percentile"),percentiles[11]));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "65th Percentile"),percentiles[12]));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "70th Percentile"), percentiles[13]));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "75th Percentile"), percentiles[14]));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "80th Percentile"), percentiles[15]));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "85th Percentile"), percentiles[16]));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "90th Percentile"), percentiles[17]));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "95th Percentile"), percentiles[18]));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Maximum"), maximum));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Interquantile range"), interquantileRange));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Range"), maximum-minimum));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Mean absolute deviation"), meanAbsoluteDeviation));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Robust mean absolute deviation"), robustMeanAbsoluteDeviation));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Median absolute deviation"), medianAbsoluteDeviation));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Coefficient of variation"), coefficientOfVariation));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Quantile coefficient of dispersion"), quantileCoefficientOfDispersion));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Energy"), energy));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Root mean square"), rootMeanSquare));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Standard Deviation"), std::sqrt(variance)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Kurtosis"), kurtosis));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Robust mean"), robustMean));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Robust variance"), robustVariance));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Covered image intensity range"), coveredImageRange));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Mode index"), modeIdx));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Mode value"), params.quantifier->IndexToMeanIntensity(modeIdx)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Mode probability"), histogram[modeIdx] / (1.0*numberOfVoxels)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Entropy"), entropy));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Uniformtiy"), uniformity));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Number of voxels"), numberOfVoxels));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Sum of voxels"), sum));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Voxel space"), voxelSpace));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Voxel volume"), voxelVolume));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Image Dimension"), VImageDimension));
return;
}
mitk::GIFFirstOrderNumericStatistics::GIFFirstOrderNumericStatistics()
{
SetShortName("fon");
SetLongName("first-order-numeric");
SetFeatureClassName("First Order Numeric");
}
void mitk::GIFFirstOrderNumericStatistics::AddArguments(mitkCommandLineParser& parser) const
{
this->AddQuantifierArguments(parser);
std::string name = this->GetOptionPrefix();
parser.addArgument(this->GetLongName(), name, mitkCommandLineParser::Bool, "Use first order statistics (numericaly) as feature", "calculates first order statistics based features", us::Any());
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFFirstOrderNumericStatistics::DoCalculateFeatures(const Image* image, const Image* mask)
{
FeatureListType featureList;
this->InitializeQuantifier(image, mask);
MITK_INFO << "Start calculating first order features ....";
FirstOrderNumericParameterStruct params;
params.quantifier = GetQuantifier();
params.MinimumIntensity = GetQuantifier()->GetMinimum();
params.MaximumIntensity = GetQuantifier()->GetMaximum();
params.Bins = GetQuantifier()->GetBins();
params.id = this->CreateTemplateFeatureID();
AccessByItk_3(image, CalculateFirstOrderStatistics, mask, featureList, params);
MITK_INFO << "Finished calculating first order features....";
return featureList;
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFFirstOrderNumericStatistics::CalculateFeatures(const Image* image, const Image*, const Image* maskNoNAN)
{
return Superclass::CalculateFeatures(image, maskNoNAN);
}
diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderStatistics.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderStatistics.cpp
index 2254e01b22..8cf6f216d6 100644
--- a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderStatistics.cpp
+++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderStatistics.cpp
@@ -1,309 +1,307 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <mitkGIFFirstOrderStatistics.h>
// MITK
#include <mitkITKImageImport.h>
#include <mitkImageCast.h>
#include <mitkImageAccessByItk.h>
// ITK
#include <itkLabelStatisticsImageFilter.h>
#include <itkMinimumMaximumImageCalculator.h>
// STL
#include <sstream>
namespace mitk
{
struct GIFFirstOrderStatisticsParameterStruct
{
double MinimumIntensity;
double MaximumIntensity;
int Bins;
FeatureID id;
};
}
template<typename TPixel, unsigned int VImageDimension>
void
CalculateFirstOrderStatistics(const itk::Image<TPixel, VImageDimension>* itkImage, const mitk::Image* mask, mitk::GIFFirstOrderStatistics::FeatureListType & featureList, mitk::GIFFirstOrderStatisticsParameterStruct params)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::Image<unsigned short, VImageDimension> MaskType;
typedef itk::LabelStatisticsImageFilter<ImageType, MaskType> FilterType;
typedef typename FilterType::HistogramType HistogramType;
typedef typename HistogramType::IndexType HIndexType;
typedef itk::MinimumMaximumImageCalculator<ImageType> MinMaxComputerType;
typename MaskType::Pointer maskImage = MaskType::New();
mitk::CastToItkImage(mask, maskImage);
double voxelVolume = 1;
for (unsigned int i = 0; i < std::min<unsigned int>(3, VImageDimension); ++i)
voxelVolume *= itkImage->GetSpacing()[i];
double voxelSpace = 1;
for (unsigned int i = 0; i < VImageDimension; ++i)
voxelSpace *= itkImage->GetSpacing()[i];
typename MinMaxComputerType::Pointer minMaxComputer = MinMaxComputerType::New();
minMaxComputer->SetImage(itkImage);
minMaxComputer->Compute();
double imageRange = minMaxComputer->GetMaximum() - minMaxComputer->GetMinimum();
typename FilterType::Pointer labelStatisticsImageFilter = FilterType::New();
labelStatisticsImageFilter->SetInput( itkImage );
labelStatisticsImageFilter->SetLabelInput(maskImage);
labelStatisticsImageFilter->SetUseHistograms(true);
double min = params.MinimumIntensity;
double max = params.MaximumIntensity;
labelStatisticsImageFilter->SetHistogramParameters(params.Bins, min,max);
labelStatisticsImageFilter->Update();
// --------------- Range --------------------
double range = labelStatisticsImageFilter->GetMaximum(1) - labelStatisticsImageFilter->GetMinimum(1);
// --------------- Uniformity, Entropy --------------------
double count = labelStatisticsImageFilter->GetCount(1);
//double std_dev = labelStatisticsImageFilter->GetSigma(1);
double mean = labelStatisticsImageFilter->GetMean(1);
double median = labelStatisticsImageFilter->GetMedian(1);
auto histogram = labelStatisticsImageFilter->GetHistogram(1);
bool histogramIsCalculated = histogram;
HIndexType index;
index.SetSize(1);
double uniformity = 0;
double entropy = 0;
double squared_sum = 0;
double kurtosis = 0;
double mean_absolut_deviation = 0;
double median_absolut_deviation = 0;
double skewness = 0;
- double sum_prob = 0;
double binWidth = 0;
double p05th = 0, p10th = 0, p15th = 0, p20th = 0, p25th = 0, p30th = 0, p35th = 0, p40th = 0, p45th = 0, p50th = 0;
double p55th = 0, p60th = 0, p65th = 0, p70th = 0, p75th = 0, p80th = 0, p85th = 0, p90th = 0, p95th = 0;
double voxelValue = 0;
if (histogramIsCalculated)
{
binWidth = histogram->GetBinMax(0, 0) - histogram->GetBinMin(0, 0);
p05th = histogram->Quantile(0, 0.05);
p10th = histogram->Quantile(0, 0.10);
p15th = histogram->Quantile(0, 0.15);
p20th = histogram->Quantile(0, 0.20);
p25th = histogram->Quantile(0, 0.25);
p30th = histogram->Quantile(0, 0.30);
p35th = histogram->Quantile(0, 0.35);
p40th = histogram->Quantile(0, 0.40);
p45th = histogram->Quantile(0, 0.45);
p50th = histogram->Quantile(0, 0.50);
p55th = histogram->Quantile(0, 0.55);
p60th = histogram->Quantile(0, 0.60);
p65th = histogram->Quantile(0, 0.65);
p70th = histogram->Quantile(0, 0.70);
p75th = histogram->Quantile(0, 0.75);
p80th = histogram->Quantile(0, 0.80);
p85th = histogram->Quantile(0, 0.85);
p90th = histogram->Quantile(0, 0.90);
p95th = histogram->Quantile(0, 0.95);
}
double Log2=log(2);
double mode_bin;
double mode_value = 0;
double variance = 0;
if (histogramIsCalculated)
{
for (int i = 0; i < (int)(histogram->GetSize(0)); ++i)
{
index[0] = i;
double prob = histogram->GetFrequency(index);
if (prob < 0.00000001)
continue;
voxelValue = histogram->GetBinMin(0, i) + binWidth * 0.5;
if (prob > mode_value)
{
mode_value = prob;
mode_bin = voxelValue;
}
- sum_prob += prob;
squared_sum += prob * voxelValue*voxelValue;
prob /= count;
mean_absolut_deviation += prob* std::abs(voxelValue - mean);
median_absolut_deviation += prob* std::abs(voxelValue - median);
variance += prob * (voxelValue - mean) * (voxelValue - mean);
kurtosis += prob* (voxelValue - mean) * (voxelValue - mean) * (voxelValue - mean) * (voxelValue - mean);
skewness += prob* (voxelValue - mean) * (voxelValue - mean) * (voxelValue - mean);
uniformity += prob*prob;
if (prob > 0)
{
entropy += prob * std::log(prob) / Log2;
}
}
}
entropy = -entropy;
double uncorrected_std_dev = std::sqrt(variance);
double rms = std::sqrt(squared_sum / count);
kurtosis = kurtosis / (variance * variance);
skewness = skewness / (variance * uncorrected_std_dev);
double coveredGrayValueRange = range / imageRange;
double coefficient_of_variation = (mean == 0) ? 0 : std::sqrt(variance) / mean;
double quantile_coefficient_of_dispersion = (p75th - p25th) / (p75th + p25th);
//Calculate the robust mean absolute deviation
//First, set all frequencies to 0 that are <10th or >90th percentile
double meanRobust = 0.0;
double robustMeanAbsoluteDeviation = 0.0;
if (histogramIsCalculated)
{
for (int i = 0; i < (int)(histogram->GetSize(0)); ++i)
{
index[0] = i;
if (histogram->GetBinMax(0, i) < p10th)
{
histogram->SetFrequencyOfIndex(index, 0);
}
else if (histogram->GetBinMin(0, i) > p90th)
{
histogram->SetFrequencyOfIndex(index, 0);
}
}
//Calculate the mean
for (int i = 0; i < (int)(histogram->GetSize(0)); ++i)
{
index[0] = i;
meanRobust += histogram->GetFrequency(index) * 0.5 * (histogram->GetBinMin(0, i) + histogram->GetBinMax(0, i));
}
meanRobust = meanRobust / histogram->GetTotalFrequency();
for (int i = 0; i < (int)(histogram->GetSize(0)); ++i)
{
index[0] = i;
robustMeanAbsoluteDeviation += std::abs(histogram->GetFrequency(index) *
((0.5 * (histogram->GetBinMin(0, i) + histogram->GetBinMax(0, i)))
- meanRobust
));
}
robustMeanAbsoluteDeviation = robustMeanAbsoluteDeviation / histogram->GetTotalFrequency();
}
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Mean"), labelStatisticsImageFilter->GetMean(1)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Unbiased Variance"), labelStatisticsImageFilter->GetVariance(1))); //Siehe Definition von Unbiased Variance estimation. (Wird nicht durch n sondern durch n-1 normalisiert)
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Biased Variance"), variance));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Skewness"), skewness));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Kurtosis"), kurtosis));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Median"), labelStatisticsImageFilter->GetMedian(1)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Minimum"), labelStatisticsImageFilter->GetMinimum(1)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Maximum"), labelStatisticsImageFilter->GetMaximum(1)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Range"), range));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Mean Absolute Deviation"), mean_absolut_deviation));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Robust Mean Absolute Deviation"), robustMeanAbsoluteDeviation));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Median Absolute Deviation"), median_absolut_deviation));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Coefficient Of Variation"), coefficient_of_variation));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Quantile Coefficient Of Dispersion"), quantile_coefficient_of_dispersion));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Energy"), squared_sum));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Root Mean Square"), rms));
typename HistogramType::MeasurementVectorType mv(1);
mv[0] = 0;
typename HistogramType::IndexType resultingIndex;
histogram->GetIndex(mv, resultingIndex);
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Robust Mean"), meanRobust));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Uniformity"), uniformity));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Entropy"), entropy));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Excess Kurtosis"), kurtosis - 3));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Covered Image Intensity Range"), coveredGrayValueRange));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Sum"), labelStatisticsImageFilter->GetSum(1)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Mode"), mode_bin));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Mode Probability"), mode_value));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Unbiased Standard deviation"), labelStatisticsImageFilter->GetSigma(1)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Biased Standard deviation"), sqrt(variance)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Number Of Voxels"), labelStatisticsImageFilter->GetCount(1)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"05th Percentile"), p05th));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"10th Percentile"), p10th));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"15th Percentile"), p15th));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"20th Percentile"), p20th));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"25th Percentile"), p25th));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"30th Percentile"), p30th));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"35th Percentile"), p35th));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"40th Percentile"), p40th));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"45th Percentile"), p45th));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"50th Percentile"), p50th));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"55th Percentile"), p55th));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"60th Percentile"), p60th));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"65th Percentile"), p65th));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"70th Percentile"), p70th));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"75th Percentile"), p75th));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"80th Percentile"), p80th));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"85th Percentile"), p85th));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"90th Percentile"), p90th));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"95th Percentile"), p95th));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Interquartile Range"), (p75th - p25th)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Image Dimension"), VImageDimension));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Voxel Space"), voxelSpace));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id,"Voxel Volume"), voxelVolume));
}
mitk::GIFFirstOrderStatistics::GIFFirstOrderStatistics()
{
SetShortName("fo");
SetLongName("first-order");
SetFeatureClassName("First Order");
}
void mitk::GIFFirstOrderStatistics::AddArguments(mitkCommandLineParser& parser) const
{
this->AddQuantifierArguments(parser);
std::string name = this->GetOptionPrefix();
parser.addArgument(this->GetLongName(), name, mitkCommandLineParser::Bool, "Use first order statistics as feature", "calculates first order statistics features", us::Any());
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFFirstOrderStatistics::DoCalculateFeatures(const Image* image, const Image* mask)
{
FeatureListType featureList;
this->InitializeQuantifier(image, mask);
MITK_INFO << "Start calculating first order features ....";
GIFFirstOrderStatisticsParameterStruct params;
params.MinimumIntensity = GetQuantifier()->GetMinimum();
params.MaximumIntensity = GetQuantifier()->GetMaximum();
params.Bins = GetQuantifier()->GetBins();
params.id = this->CreateTemplateFeatureID();
AccessByItk_3(image, CalculateFirstOrderStatistics, mask, featureList, params);
MITK_INFO << "Finished calculating first order features....";
return featureList;
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFFirstOrderStatistics::CalculateFeatures(const Image* image, const Image*, const Image* maskNoNAN)
{
return Superclass::CalculateFeatures(image, maskNoNAN);
}
diff --git a/Modules/Classification/CLUtilities/test/mitkGIFCurvatureStatisticTest.cpp b/Modules/Classification/CLUtilities/test/mitkGIFCurvatureStatisticTest.cpp
index d56d087a0d..f4fc5fadf1 100644
--- a/Modules/Classification/CLUtilities/test/mitkGIFCurvatureStatisticTest.cpp
+++ b/Modules/Classification/CLUtilities/test/mitkGIFCurvatureStatisticTest.cpp
@@ -1,118 +1,114 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <mitkTestingMacros.h>
#include <mitkTestFixture.h>
#include "mitkIOUtil.h"
#include <cmath>
#include <mitkGIFCurvatureStatistic.h>
class mitkGIFCurvatureStatisticTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkGIFCurvatureStatisticTestSuite);
MITK_TEST(ImageDescription_PhantomTest);
CPPUNIT_TEST_SUITE_END();
private:
- mitk::Image::Pointer m_IBSI_Phantom_Image_Small;
mitk::Image::Pointer m_IBSI_Phantom_Image_Large;
- mitk::Image::Pointer m_IBSI_Phantom_Mask_Small;
mitk::Image::Pointer m_IBSI_Phantom_Mask_Large;
public:
void setUp(void) override
{
- m_IBSI_Phantom_Image_Small = mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("Radiomics/IBSI_Phantom_Image_Small.nrrd"));
m_IBSI_Phantom_Image_Large = mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("Radiomics/IBSI_Phantom_Image_Large.nrrd"));
- m_IBSI_Phantom_Mask_Small = mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("Radiomics/IBSI_Phantom_Mask_Small.nrrd"));
m_IBSI_Phantom_Mask_Large = mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("Radiomics/IBSI_Phantom_Mask_Large.nrrd"));
}
void ImageDescription_PhantomTest()
{
mitk::GIFCurvatureStatistic::Pointer featureCalculator = mitk::GIFCurvatureStatistic::New();
featureCalculator->SetUseBinsize(true);
featureCalculator->SetBinsize(1.0);
featureCalculator->SetUseMinimumIntensity(true);
featureCalculator->SetUseMaximumIntensity(true);
featureCalculator->SetMinimumIntensity(0.5);
featureCalculator->SetMaximumIntensity(6.5);
auto featureList = featureCalculator->CalculateFeatures(m_IBSI_Phantom_Image_Large, m_IBSI_Phantom_Mask_Large);
std::map<std::string, double> results;
for (const auto &valuePair : featureList)
{
MITK_INFO << mitk::AbstractGlobalImageFeature::GenerateLegacyFeatureNameWOEncoding(valuePair.first) << " : " << valuePair.second;
results[mitk::AbstractGlobalImageFeature::GenerateLegacyFeatureNameWOEncoding(valuePair.first)] = valuePair.second;
}
CPPUNIT_ASSERT_EQUAL_MESSAGE("Image Diagnostics should calculate 44 features.", std::size_t(44), featureList.size());
// These values are obtained by a run of the filter.
// The might be wrong!
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Minimum Mean Curvature with Large IBSI Phantom Image", -1.51, results["Curvature Feature::Minimum Mean Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Maximum Mean Curvature with Large IBSI Phantom Image", 0.51, results["Curvature Feature::Maximum Mean Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Mean Mean Curvature with Large IBSI Phantom Image", 0.095, results["Curvature Feature::Mean Mean Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Standard Deviation Mean Curvature with Large IBSI Phantom Image", 0.45, results["Curvature Feature::Standard Deviation Mean Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Skewness Mean Curvature with Large IBSI Phantom Image", -2.55, results["Curvature Feature::Skewness Mean Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Mean Positive Mean Curvature with Large IBSI Phantom Image", 0.30, results["Curvature Feature::Mean Positive Mean Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Standard Deviation Positive Mean Curvature with Large IBSI Phantom Image", 0.12, results["Curvature Feature::Standard Deviation Positive Mean Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Skewness Positive Mean Curvature with Large IBSI Phantom Image", 0.60, results["Curvature Feature::Skewness Positive Mean Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Mean Negative Mean Curvature with Large IBSI Phantom Image", -0.955, results["Curvature Feature::Mean Negative Mean Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Standard Deviation Negative Mean Curvature with Large IBSI Phantom Image", 0.56, results["Curvature Feature::Standard Deviation Negative Mean Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Skewness Negative Mean Curvature with Large IBSI Phantom Image", 0.09, results["Curvature Feature::Skewness Negative Mean Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Minimum Gaussian Curvature with Large IBSI Phantom Image", -0.211, results["Curvature Feature::Minimum Gaussian Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Maximum Gaussian Curvature with Large IBSI Phantom Image", 1.81, results["Curvature Feature::Maximum Gaussian Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Mean Gaussian Curvature with Large IBSI Phantom Image", 0.14, results["Curvature Feature::Mean Gaussian Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Standard Deviation Gaussian Curvature with Large IBSI Phantom Image", 0.42, results["Curvature Feature::Standard Deviation Gaussian Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Skewness Gaussian Curvature with Large IBSI Phantom Image", 3.51, results["Curvature Feature::Skewness Gaussian Curvature"], 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Mean Positive Gaussian Curvature with Large IBSI Phantom Image", 0.26, results["Curvature Feature::Mean Positive Gaussian Curvature"], 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Standard Deviation Positive Gaussian Curvature with Large IBSI Phantom Image", 0.53, results["Curvature Feature::Standard Deviation Positive Gaussian Curvature"], 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Skewness Positive Gaussian Curvature with Large IBSI Phantom Image", 2.52, results["Curvature Feature::Skewness Positive Gaussian Curvature"], 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Mean Negative Gaussian Curvature with Large IBSI Phantom Image", -0.03, results["Curvature Feature::Mean Negative Gaussian Curvature"], 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Standard Deviation Negative Gaussian Curvature with Large IBSI Phantom Image", 0.055, results["Curvature Feature::Standard Deviation Negative Gaussian Curvature"], 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Skewness Negative Gaussian Curvature with Large IBSI Phantom Image", -1.92, results["Curvature Feature::Skewness Negative Gaussian Curvature"], 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Mean Positive Gaussian Curvature with Large IBSI Phantom Image", 0.38, results["Curvature Feature::Mean Positive Gaussian Curvature"], 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Standard Deviation Positive Gaussian Curvature with Large IBSI Phantom Image", 0.60, results["Curvature Feature::Standard Deviation Positive Gaussian Curvature"], 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Skewness Positive Gaussian Curvature with Large IBSI Phantom Image", 1.89, results["Curvature Feature::Skewness Positive Gaussian Curvature"], 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Mean Negative Gaussian Curvature with Large IBSI Phantom Image", -0.11, results["Curvature Feature::Mean Negative Gaussian Curvature"], 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Standard Deviation Negative Gaussian Curvature with Large IBSI Phantom Image", 0.049, results["Curvature Feature::Standard Deviation Negative Gaussian Curvature"], 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Skewness Negative Gaussian Curvature with Large IBSI Phantom Image", -0.74, results["Curvature Feature::Skewness Negative Gaussian Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Minimum Minimum Curvature with Large IBSI Phantom Image", -2.19, results["Curvature Feature::Minimum Minimum Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Maximum Minimum Curvature with Large IBSI Phantom Image", 0.35, results["Curvature Feature::Maximum Minimum Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Mean Minimum Curvature with Large IBSI Phantom Image", -0.11, results["Curvature Feature::Mean Minimum Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Standard Deviation Minimum Curvature with Large IBSI Phantom Image", 0.573, results["Curvature Feature::Standard Deviation Minimum Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Skewness Minimum Curvature with Large IBSI Phantom Image", -2.742, results["Curvature Feature::Skewness Minimum Curvature"], 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Mean Positive Minimum Curvature with Large IBSI Phantom Image", 0.161, results["Curvature Feature::Mean Positive Minimum Curvature"], 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Standard Deviation Positive Minimum Curvature with Large IBSI Phantom Image", 0.165, results["Curvature Feature::Standard Deviation Positive Minimum Curvature"], 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Skewness Positive Minimum Curvature with Large IBSI Phantom Image", 0.108, results["Curvature Feature::Skewness Positive Minimum Curvature"], 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Mean Negative Minimum Curvature with Large IBSI Phantom Image", -0.42, results["Curvature Feature::Mean Negative Minimum Curvature"], 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Standard Deviation Negative Minimum Curvature with Large IBSI Phantom Image", 0.733, results["Curvature Feature::Standard Deviation Negative Minimum Curvature"], 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Skewness Negative Minimum Curvature with Large IBSI Phantom Image", -1.73, results["Curvature Feature::Skewness Negative Minimum Curvature"], 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Mean Positive Minimum Curvature with Large IBSI Phantom Image", 0.264, results["Curvature Feature::Mean Positive Minimum Curvature"], 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Standard Deviation Positive Minimum Curvature with Large IBSI Phantom Image", 0.132, results["Curvature Feature::Standard Deviation Positive Minimum Curvature"], 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Skewness Positive Minimum Curvature with Large IBSI Phantom Image", -1.335, results["Curvature Feature::Skewness Positive Minimum Curvature"], 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Mean Negative Minimum Curvature with Large IBSI Phantom Image", -0.946, results["Curvature Feature::Mean Negative Minimum Curvature"], 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Standard Deviation Negative Minimum Curvature with Large IBSI Phantom Image", 0.844, results["Curvature Feature::Standard Deviation Negative Minimum Curvature"], 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Skewness Negative Minimum Curvature with Large IBSI Phantom Image", -0.599, results["Curvature Feature::Skewness Negative Minimum Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Minimum Maximum Curvature with Large IBSI Phantom Image", -0.83, results["Curvature Feature::Minimum Maximum Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Maximum Maximum Curvature with Large IBSI Phantom Image", 0.79, results["Curvature Feature::Maximum Maximum Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Mean Maximum Curvature with Large IBSI Phantom Image", 0.30, results["Curvature Feature::Mean Maximum Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Standard Deviation Maximum Curvature with Large IBSI Phantom Image", 0.369, results["Curvature Feature::Standard Deviation Maximum Curvature"], 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Skewness Maximum Curvature with Large IBSI Phantom Image", -1.617, results["Curvature Feature::Skewness Maximum Curvature"], 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Mean Positive Maximum Curvature with Large IBSI Phantom Image", 0.419, results["Curvature Feature::Mean Positive Maximum Curvature"], 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Standard Deviation Positive Maximum Curvature with Large IBSI Phantom Image", 0.217, results["Curvature Feature::Standard Deviation Positive Maximum Curvature"], 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Skewness Positive Maximum Curvature with Large IBSI Phantom Image", -0.958, results["Curvature Feature::Skewness Positive Maximum Curvature"], 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Mean Negative Maximum Curvature with Large IBSI Phantom Image", -0.44, results["Curvature Feature::Mean Negative Maximum Curvature"], 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Standard Deviation Negative Maximum Curvature with Large IBSI Phantom Image", 0.399, results["Curvature Feature::Standard Deviation Negative Maximum Curvature"], 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Skewness Negative Maximum Curvature with Large IBSI Phantom Image", 0.109, results["Curvature Feature::Skewness Negative Maximum Curvature"], 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Mean Positive Maximum Curvature with Large IBSI Phantom Image", 0.513, results["Curvature Feature::Mean Positive Maximum Curvature"], 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Standard Deviation Positive Maximum Curvature with Large IBSI Phantom Image", 0.094, results["Curvature Feature::Standard Deviation Positive Maximum Curvature"], 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Skewness Positive Maximum Curvature with Large IBSI Phantom Image", 1.548, results["Curvature Feature::Skewness Positive Maximum Curvature"], 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Mean Negative Maximum Curvature with Large IBSI Phantom Image", -0.47, results["Curvature Feature::Mean Negative Maximum Curvature"], 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Standard Deviation Negative Maximum Curvature with Large IBSI Phantom Image", 0.394, results["Curvature Feature::Standard Deviation Negative Maximum Curvature"], 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Curvature Feature::Skewness Negative Maximum Curvature with Large IBSI Phantom Image", 0.273, results["Curvature Feature::Skewness Negative Maximum Curvature"], 0.01);
}
};
MITK_TEST_SUITE_REGISTRATION(mitkGIFCurvatureStatistic )
diff --git a/Modules/Classification/CLVigraRandomForest/include/mitkPURFClassifier.h b/Modules/Classification/CLVigraRandomForest/include/mitkPURFClassifier.h
index 4eadac9ea3..71e3397d06 100644
--- a/Modules/Classification/CLVigraRandomForest/include/mitkPURFClassifier.h
+++ b/Modules/Classification/CLVigraRandomForest/include/mitkPURFClassifier.h
@@ -1,93 +1,93 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkPURFClassifier_h
#define mitkPURFClassifier_h
#include <MitkCLVigraRandomForestExports.h>
#include <mitkAbstractClassifier.h>
//#include <vigra/multi_array.hxx>
#include <vigra/random_forest.hxx>
#include <mitkBaseData.h>
namespace mitk
{
class MITKCLVIGRARANDOMFOREST_EXPORT PURFClassifier : public AbstractClassifier
{
public:
mitkClassMacro(PURFClassifier, AbstractClassifier);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
PURFClassifier();
~PURFClassifier() override;
void Train(const Eigen::MatrixXd &X, const Eigen::MatrixXi &Y) override;
Eigen::MatrixXi Predict(const Eigen::MatrixXd &X) override;
Eigen::MatrixXi PredictWeighted(const Eigen::MatrixXd &X);
bool SupportsPointWiseWeight() override;
bool SupportsPointWiseProbability() override;
void ConvertParameter();
vigra::ArrayVector<double> CalculateKappa(const Eigen::MatrixXd & X_in, const Eigen::MatrixXi &Y_in);
void SetRandomForest(const vigra::RandomForest<int> & rf);
const vigra::RandomForest<int> & GetRandomForest() const;
void UsePointWiseWeight(bool) override;
void SetMaximumTreeDepth(int);
void SetMinimumSplitNodeSize(int);
void SetPrecision(double);
void SetSamplesPerTree(double);
void UseSampleWithReplacement(bool);
void SetTreeCount(int);
void SetWeightLambda(double);
void PrintParameter(std::ostream &str = std::cout);
void SetClassProbabilities(Eigen::VectorXd probabilities);
Eigen::VectorXd GetClassProbabilites();
private:
// *-------------------
// * THREADING
// *-------------------
struct TrainingData;
struct PredictionData;
struct EigenToVigraTransform;
struct Parameter;
vigra::MultiArrayView<2, double> m_Probabilities;
Eigen::MatrixXd m_TreeWeights;
Eigen::VectorXd m_ClassProbabilities;
Parameter * m_Parameter;
vigra::RandomForest<int> m_RandomForest;
- static ITK_THREAD_RETURN_TYPE TrainTreesCallback(void *);
- static ITK_THREAD_RETURN_TYPE PredictCallback(void *);
- static ITK_THREAD_RETURN_TYPE PredictWeightedCallback(void *);
+ static itk::ITK_THREAD_RETURN_TYPE TrainTreesCallback(void *);
+ static itk::ITK_THREAD_RETURN_TYPE PredictCallback(void *);
+ static itk::ITK_THREAD_RETURN_TYPE PredictWeightedCallback(void *);
static void VigraPredictWeighted(PredictionData *data, vigra::MultiArrayView<2, double> & X, vigra::MultiArrayView<2, int> & Y, vigra::MultiArrayView<2, double> & P);
};
}
#endif //mitkPURFClassifier_h
diff --git a/Modules/Classification/CLVigraRandomForest/include/mitkVigraRandomForestClassifier.h b/Modules/Classification/CLVigraRandomForest/include/mitkVigraRandomForestClassifier.h
index b167df6c1e..13df06b8bd 100644
--- a/Modules/Classification/CLVigraRandomForest/include/mitkVigraRandomForestClassifier.h
+++ b/Modules/Classification/CLVigraRandomForest/include/mitkVigraRandomForestClassifier.h
@@ -1,92 +1,92 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkVigraRandomForestClassifier_h
#define mitkVigraRandomForestClassifier_h
#include <MitkCLVigraRandomForestExports.h>
#include <mitkAbstractClassifier.h>
//#include <vigra/multi_array.hxx>
#include <vigra/random_forest.hxx>
#include <mitkBaseData.h>
namespace mitk
{
class MITKCLVIGRARANDOMFOREST_EXPORT VigraRandomForestClassifier : public AbstractClassifier
{
public:
mitkClassMacro(VigraRandomForestClassifier, AbstractClassifier);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
VigraRandomForestClassifier();
~VigraRandomForestClassifier() override;
void Train(const Eigen::MatrixXd &X, const Eigen::MatrixXi &Y) override;
void OnlineTrain(const Eigen::MatrixXd &X, const Eigen::MatrixXi &Y);
Eigen::MatrixXi Predict(const Eigen::MatrixXd &X) override;
Eigen::MatrixXi PredictWeighted(const Eigen::MatrixXd &X);
bool SupportsPointWiseWeight() override;
bool SupportsPointWiseProbability() override;
void ConvertParameter();
void SetRandomForest(const vigra::RandomForest<int> & rf);
const vigra::RandomForest<int> & GetRandomForest() const;
void UsePointWiseWeight(bool) override;
void SetMaximumTreeDepth(int);
void SetMinimumSplitNodeSize(int);
void SetPrecision(double);
void SetSamplesPerTree(double);
void UseSampleWithReplacement(bool);
void SetTreeCount(int);
void SetWeightLambda(double);
void SetTreeWeights(Eigen::MatrixXd weights);
void SetTreeWeight(int treeId, double weight);
Eigen::MatrixXd GetTreeWeights() const;
void PrintParameter(std::ostream &str = std::cout);
private:
// *-------------------
// * THREADING
// *-------------------
struct TrainingData;
struct PredictionData;
struct EigenToVigraTransform;
struct Parameter;
vigra::MultiArrayView<2, double> m_Probabilities;
Eigen::MatrixXd m_TreeWeights;
Parameter * m_Parameter;
vigra::RandomForest<int> m_RandomForest;
- static ITK_THREAD_RETURN_TYPE TrainTreesCallback(void *);
- static ITK_THREAD_RETURN_TYPE PredictCallback(void *);
- static ITK_THREAD_RETURN_TYPE PredictWeightedCallback(void *);
+ static itk::ITK_THREAD_RETURN_TYPE TrainTreesCallback(void *);
+ static itk::ITK_THREAD_RETURN_TYPE PredictCallback(void *);
+ static itk::ITK_THREAD_RETURN_TYPE PredictWeightedCallback(void *);
static void VigraPredictWeighted(PredictionData *data, vigra::MultiArrayView<2, double> & X, vigra::MultiArrayView<2, int> & Y, vigra::MultiArrayView<2, double> & P);
};
}
#endif //mitkVigraRandomForestClassifier_h
diff --git a/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkPURFClassifier.cpp b/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkPURFClassifier.cpp
index f686e14da4..05f8414cc7 100644
--- a/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkPURFClassifier.cpp
+++ b/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkPURFClassifier.cpp
@@ -1,474 +1,474 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// MITK includes
#include <mitkPURFClassifier.h>
#include <mitkThresholdSplit.h>
#include <mitkPUImpurityLoss.h>
#include <mitkImpurityLoss.h>
#include <mitkLinearSplitting.h>
#include <mitkProperties.h>
// Vigra includes
#include <vigra/random_forest.hxx>
#include <vigra/random_forest/rf_split.hxx>
// ITK include
-#include <itkFastMutexLock.h>
-#include <itkMultiThreader.h>
+#include <itkMultiThreaderBase.h>
#include <itkCommand.h>
+#include <mutex>
+
typedef mitk::ThresholdSplit<mitk::LinearSplitting< mitk::PUImpurityLoss<> >,int,vigra::ClassificationTag> DefaultPUSplitType;
struct mitk::PURFClassifier::Parameter
{
vigra::RF_OptionTag Stratification;
bool SampleWithReplacement;
bool UseRandomSplit;
bool UsePointBasedWeights;
int TreeCount;
int MinimumSplitNodeSize;
int TreeDepth;
double Precision;
double WeightLambda;
double SamplesPerTree;
};
struct mitk::PURFClassifier::TrainingData
{
TrainingData(unsigned int numberOfTrees,
const vigra::RandomForest<int> & refRF,
const DefaultPUSplitType & refSplitter,
const vigra::MultiArrayView<2, double> refFeature,
const vigra::MultiArrayView<2, int> refLabel,
const Parameter parameter)
: m_ClassCount(0),
m_NumberOfTrees(numberOfTrees),
m_RandomForest(refRF),
m_Splitter(refSplitter),
m_Feature(refFeature),
m_Label(refLabel),
m_Parameter(parameter)
{
- m_mutex = itk::FastMutexLock::New();
}
vigra::ArrayVector<vigra::RandomForest<int>::DecisionTree_t> trees_;
int m_ClassCount;
unsigned int m_NumberOfTrees;
const vigra::RandomForest<int> & m_RandomForest;
const DefaultPUSplitType & m_Splitter;
const vigra::MultiArrayView<2, double> m_Feature;
const vigra::MultiArrayView<2, int> m_Label;
- itk::FastMutexLock::Pointer m_mutex;
+ std::mutex m_mutex;
Parameter m_Parameter;
};
struct mitk::PURFClassifier::PredictionData
{
PredictionData(const vigra::RandomForest<int> & refRF,
const vigra::MultiArrayView<2, double> refFeature,
vigra::MultiArrayView<2, int> refLabel,
vigra::MultiArrayView<2, double> refProb,
vigra::MultiArrayView<2, double> refTreeWeights)
: m_RandomForest(refRF),
m_Feature(refFeature),
m_Label(refLabel),
m_Probabilities(refProb),
m_TreeWeights(refTreeWeights)
{
}
const vigra::RandomForest<int> & m_RandomForest;
const vigra::MultiArrayView<2, double> m_Feature;
vigra::MultiArrayView<2, int> m_Label;
vigra::MultiArrayView<2, double> m_Probabilities;
vigra::MultiArrayView<2, double> m_TreeWeights;
};
mitk::PURFClassifier::PURFClassifier()
:m_Parameter(nullptr)
{
itk::SimpleMemberCommand<mitk::PURFClassifier>::Pointer command = itk::SimpleMemberCommand<mitk::PURFClassifier>::New();
command->SetCallbackFunction(this, &mitk::PURFClassifier::ConvertParameter);
this->GetPropertyList()->AddObserver( itk::ModifiedEvent(), command );
}
mitk::PURFClassifier::~PURFClassifier()
{
}
void mitk::PURFClassifier::SetClassProbabilities(Eigen::VectorXd probabilities)
{
m_ClassProbabilities = probabilities;
}
Eigen::VectorXd mitk::PURFClassifier::GetClassProbabilites()
{
return m_ClassProbabilities;
}
bool mitk::PURFClassifier::SupportsPointWiseWeight()
{
return true;
}
bool mitk::PURFClassifier::SupportsPointWiseProbability()
{
return true;
}
vigra::ArrayVector<double> mitk::PURFClassifier::CalculateKappa(const Eigen::MatrixXd & /* X_in */, const Eigen::MatrixXi & Y_in)
{
int maximumValue = Y_in.maxCoeff();
vigra::ArrayVector<double> kappa(maximumValue + 1);
vigra::ArrayVector<double> counts(maximumValue + 1);
for (int i = 0; i < Y_in.rows(); ++i)
{
counts[Y_in(i, 0)] += 1;
}
for (int i = 0; i < maximumValue+1; ++i)
{
if (counts[i] > 0)
{
kappa[i] = counts[0] * m_ClassProbabilities[i] / counts[i] + 1;
}
else
{
kappa[i] = 1;
}
}
return kappa;
}
void mitk::PURFClassifier::Train(const Eigen::MatrixXd & X_in, const Eigen::MatrixXi &Y_in)
{
this->ConvertParameter();
PURFData* purfData = new PURFData;
purfData->m_Kappa = this->CalculateKappa(X_in, Y_in);
DefaultPUSplitType splitter;
splitter.UsePointBasedWeights(m_Parameter->UsePointBasedWeights);
splitter.UseRandomSplit(m_Parameter->UseRandomSplit);
splitter.SetPrecision(m_Parameter->Precision);
splitter.SetMaximumTreeDepth(m_Parameter->TreeDepth);
splitter.SetAdditionalData(purfData);
// Weights handled as member variable
if (m_Parameter->UsePointBasedWeights)
{
// Set influence of the weight (0 no influenc to 1 max influence)
this->m_PointWiseWeight.unaryExpr([this](double t){ return std::pow(t, this->m_Parameter->WeightLambda) ;});
vigra::MultiArrayView<2, double> W(vigra::Shape2(this->m_PointWiseWeight.rows(),this->m_PointWiseWeight.cols()),this->m_PointWiseWeight.data());
splitter.SetWeights(W);
}
vigra::MultiArrayView<2, double> X(vigra::Shape2(X_in.rows(),X_in.cols()),X_in.data());
vigra::MultiArrayView<2, int> Y(vigra::Shape2(Y_in.rows(),Y_in.cols()),Y_in.data());
m_RandomForest.set_options().tree_count(1); // Number of trees that are calculated;
m_RandomForest.set_options().use_stratification(m_Parameter->Stratification);
m_RandomForest.set_options().sample_with_replacement(m_Parameter->SampleWithReplacement);
m_RandomForest.set_options().samples_per_tree(m_Parameter->SamplesPerTree);
m_RandomForest.set_options().min_split_node_size(m_Parameter->MinimumSplitNodeSize);
m_RandomForest.learn(X, Y,vigra::rf::visitors::VisitorBase(),splitter);
std::unique_ptr<TrainingData> data(new TrainingData(m_Parameter->TreeCount,m_RandomForest,splitter,X,Y, *m_Parameter));
- itk::MultiThreader::Pointer threader = itk::MultiThreader::New();
+ auto threader = itk::MultiThreaderBase::New();
threader->SetSingleMethod(this->TrainTreesCallback,data.get());
threader->SingleMethodExecute();
// set result trees
m_RandomForest.set_options().tree_count(m_Parameter->TreeCount);
m_RandomForest.ext_param_.class_count_ = data->m_ClassCount;
m_RandomForest.trees_ = data->trees_;
// Set Tree Weights to default
m_TreeWeights = Eigen::MatrixXd(m_Parameter->TreeCount,1);
m_TreeWeights.fill(1.0);
delete purfData;
}
Eigen::MatrixXi mitk::PURFClassifier::Predict(const Eigen::MatrixXd &X_in)
{
// Initialize output Eigen matrices
m_OutProbability = Eigen::MatrixXd(X_in.rows(),m_RandomForest.class_count());
m_OutProbability.fill(0);
m_OutLabel = Eigen::MatrixXi(X_in.rows(),1);
m_OutLabel.fill(0);
// If no weights provided
if(m_TreeWeights.rows() != m_RandomForest.tree_count())
{
m_TreeWeights = Eigen::MatrixXd(m_RandomForest.tree_count(),1);
m_TreeWeights.fill(1);
}
vigra::MultiArrayView<2, double> P(vigra::Shape2(m_OutProbability.rows(),m_OutProbability.cols()),m_OutProbability.data());
vigra::MultiArrayView<2, int> Y(vigra::Shape2(m_OutLabel.rows(),m_OutLabel.cols()),m_OutLabel.data());
vigra::MultiArrayView<2, double> X(vigra::Shape2(X_in.rows(),X_in.cols()),X_in.data());
vigra::MultiArrayView<2, double> TW(vigra::Shape2(m_RandomForest.tree_count(),1),m_TreeWeights.data());
std::unique_ptr<PredictionData> data;
data.reset(new PredictionData(m_RandomForest, X, Y, P, TW));
- itk::MultiThreader::Pointer threader = itk::MultiThreader::New();
+ auto threader = itk::MultiThreaderBase::New();
threader->SetSingleMethod(this->PredictCallback, data.get());
threader->SingleMethodExecute();
m_Probabilities = data->m_Probabilities;
return m_OutLabel;
}
-ITK_THREAD_RETURN_TYPE mitk::PURFClassifier::TrainTreesCallback(void * arg)
+itk::ITK_THREAD_RETURN_TYPE mitk::PURFClassifier::TrainTreesCallback(void * arg)
{
// Get the ThreadInfoStruct
- typedef itk::MultiThreader::ThreadInfoStruct ThreadInfoType;
+ typedef itk::MultiThreaderBase::WorkUnitInfo ThreadInfoType;
ThreadInfoType * infoStruct = static_cast< ThreadInfoType * >( arg );
TrainingData * data = (TrainingData *)(infoStruct->UserData);
unsigned int numberOfTreesToCalculate = 0;
// define the number of tress the forest have to calculate
- numberOfTreesToCalculate = data->m_NumberOfTrees / infoStruct->NumberOfThreads;
+ numberOfTreesToCalculate = data->m_NumberOfTrees / infoStruct->NumberOfWorkUnits;
// the 0th thread takes the residuals
- if(infoStruct->ThreadID == 0) numberOfTreesToCalculate += data->m_NumberOfTrees % infoStruct->NumberOfThreads;
+ if(infoStruct->WorkUnitID == 0) numberOfTreesToCalculate += data->m_NumberOfTrees % infoStruct->NumberOfWorkUnits;
if(numberOfTreesToCalculate != 0){
// Copy the Treestructure defined in userData
vigra::RandomForest<int> rf = data->m_RandomForest;
// Initialize a splitter for the leraning process
DefaultPUSplitType splitter;
splitter.UsePointBasedWeights(data->m_Splitter.IsUsingPointBasedWeights());
splitter.UseRandomSplit(data->m_Splitter.IsUsingRandomSplit());
splitter.SetPrecision(data->m_Splitter.GetPrecision());
splitter.SetMaximumTreeDepth(data->m_Splitter.GetMaximumTreeDepth());
splitter.SetWeights(data->m_Splitter.GetWeights());
splitter.SetAdditionalData(data->m_Splitter.GetAdditionalData());
rf.trees_.clear();
rf.set_options().tree_count(numberOfTreesToCalculate);
rf.set_options().use_stratification(data->m_Parameter.Stratification);
rf.set_options().sample_with_replacement(data->m_Parameter.SampleWithReplacement);
rf.set_options().samples_per_tree(data->m_Parameter.SamplesPerTree);
rf.set_options().min_split_node_size(data->m_Parameter.MinimumSplitNodeSize);
rf.learn(data->m_Feature, data->m_Label,vigra::rf::visitors::VisitorBase(),splitter);
- data->m_mutex->Lock();
+ data->m_mutex.lock();
for(const auto & tree : rf.trees_)
data->trees_.push_back(tree);
data->m_ClassCount = rf.class_count();
- data->m_mutex->Unlock();
+ data->m_mutex.unlock();
}
- return ITK_THREAD_RETURN_VALUE;
+ return ITK_THREAD_RETURN_DEFAULT_VALUE;
}
-ITK_THREAD_RETURN_TYPE mitk::PURFClassifier::PredictCallback(void * arg)
+itk::ITK_THREAD_RETURN_TYPE mitk::PURFClassifier::PredictCallback(void * arg)
{
// Get the ThreadInfoStruct
- typedef itk::MultiThreader::ThreadInfoStruct ThreadInfoType;
+ typedef itk::MultiThreaderBase::WorkUnitInfo ThreadInfoType;
ThreadInfoType * infoStruct = static_cast< ThreadInfoType * >( arg );
// assigne the thread id
- const unsigned int threadId = infoStruct->ThreadID;
+ const unsigned int threadId = infoStruct->WorkUnitID;
// Get the user defined parameters containing all
// neccesary informations
PredictionData * data = (PredictionData *)(infoStruct->UserData);
unsigned int numberOfRowsToCalculate = 0;
// Get number of rows to calculate
- numberOfRowsToCalculate = data->m_Feature.shape()[0] / infoStruct->NumberOfThreads;
+ numberOfRowsToCalculate = data->m_Feature.shape()[0] / infoStruct->NumberOfWorkUnits;
unsigned int start_index = numberOfRowsToCalculate * threadId;
unsigned int end_index = numberOfRowsToCalculate * (threadId+1);
// the last thread takes the residuals
- if(threadId == infoStruct->NumberOfThreads-1) {
- end_index += data->m_Feature.shape()[0] % infoStruct->NumberOfThreads;
+ if(threadId == infoStruct->NumberOfWorkUnits-1) {
+ end_index += data->m_Feature.shape()[0] % infoStruct->NumberOfWorkUnits;
}
vigra::MultiArrayView<2, double> split_features;
vigra::MultiArrayView<2, int> split_labels;
vigra::MultiArrayView<2, double> split_probability;
{
vigra::TinyVector<vigra::MultiArrayIndex, 2> lowerBound(start_index,0);
vigra::TinyVector<vigra::MultiArrayIndex, 2> upperBound(end_index,data->m_Feature.shape(1));
split_features = data->m_Feature.subarray(lowerBound,upperBound);
}
{
vigra::TinyVector<vigra::MultiArrayIndex, 2> lowerBound(start_index,0);
vigra::TinyVector<vigra::MultiArrayIndex, 2> upperBound(end_index, data->m_Label.shape(1));
split_labels = data->m_Label.subarray(lowerBound,upperBound);
}
{
vigra::TinyVector<vigra::MultiArrayIndex, 2> lowerBound(start_index,0);
vigra::TinyVector<vigra::MultiArrayIndex, 2> upperBound(end_index,data->m_Probabilities.shape(1));
split_probability = data->m_Probabilities.subarray(lowerBound,upperBound);
}
data->m_RandomForest.predictLabels(split_features,split_labels);
data->m_RandomForest.predictProbabilities(split_features, split_probability);
- return ITK_THREAD_RETURN_VALUE;
+ return ITK_THREAD_RETURN_DEFAULT_VALUE;
}
void mitk::PURFClassifier::ConvertParameter()
{
if(this->m_Parameter == nullptr)
this->m_Parameter = new Parameter();
// Get the proerty // Some defaults
MITK_INFO("PURFClassifier") << "Convert Parameter";
if(!this->GetPropertyList()->Get("usepointbasedweight",this->m_Parameter->UsePointBasedWeights)) this->m_Parameter->UsePointBasedWeights = false;
if(!this->GetPropertyList()->Get("userandomsplit",this->m_Parameter->UseRandomSplit)) this->m_Parameter->UseRandomSplit = false;
if(!this->GetPropertyList()->Get("treedepth",this->m_Parameter->TreeDepth)) this->m_Parameter->TreeDepth = 20;
if(!this->GetPropertyList()->Get("treecount",this->m_Parameter->TreeCount)) this->m_Parameter->TreeCount = 100;
if(!this->GetPropertyList()->Get("minimalsplitnodesize",this->m_Parameter->MinimumSplitNodeSize)) this->m_Parameter->MinimumSplitNodeSize = 5;
if(!this->GetPropertyList()->Get("precision",this->m_Parameter->Precision)) this->m_Parameter->Precision = mitk::eps;
if(!this->GetPropertyList()->Get("samplespertree",this->m_Parameter->SamplesPerTree)) this->m_Parameter->SamplesPerTree = 0.6;
if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->SampleWithReplacement)) this->m_Parameter->SampleWithReplacement = true;
if(!this->GetPropertyList()->Get("lambda",this->m_Parameter->WeightLambda)) this->m_Parameter->WeightLambda = 1.0; // Not used yet
// if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->Stratification))
this->m_Parameter->Stratification = vigra::RF_NONE; // no Property given
}
void mitk::PURFClassifier::PrintParameter(std::ostream & str)
{
if(this->m_Parameter == nullptr)
{
MITK_WARN("PURFClassifier") << "Parameters are not initialized. Please call ConvertParameter() first!";
return;
}
this->ConvertParameter();
// Get the proerty // Some defaults
if(!this->GetPropertyList()->Get("usepointbasedweight",this->m_Parameter->UsePointBasedWeights))
str << "usepointbasedweight\tNOT SET (default " << this->m_Parameter->UsePointBasedWeights << ")" << "\n";
else
str << "usepointbasedweight\t" << this->m_Parameter->UsePointBasedWeights << "\n";
if(!this->GetPropertyList()->Get("userandomsplit",this->m_Parameter->UseRandomSplit))
str << "userandomsplit\tNOT SET (default " << this->m_Parameter->UseRandomSplit << ")" << "\n";
else
str << "userandomsplit\t" << this->m_Parameter->UseRandomSplit << "\n";
if(!this->GetPropertyList()->Get("treedepth",this->m_Parameter->TreeDepth))
str << "treedepth\t\tNOT SET (default " << this->m_Parameter->TreeDepth << ")" << "\n";
else
str << "treedepth\t\t" << this->m_Parameter->TreeDepth << "\n";
if(!this->GetPropertyList()->Get("minimalsplitnodesize",this->m_Parameter->MinimumSplitNodeSize))
str << "minimalsplitnodesize\tNOT SET (default " << this->m_Parameter->MinimumSplitNodeSize << ")" << "\n";
else
str << "minimalsplitnodesize\t" << this->m_Parameter->MinimumSplitNodeSize << "\n";
if(!this->GetPropertyList()->Get("precision",this->m_Parameter->Precision))
str << "precision\t\tNOT SET (default " << this->m_Parameter->Precision << ")" << "\n";
else
str << "precision\t\t" << this->m_Parameter->Precision << "\n";
if(!this->GetPropertyList()->Get("samplespertree",this->m_Parameter->SamplesPerTree))
str << "samplespertree\tNOT SET (default " << this->m_Parameter->SamplesPerTree << ")" << "\n";
else
str << "samplespertree\t" << this->m_Parameter->SamplesPerTree << "\n";
if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->SampleWithReplacement))
str << "samplewithreplacement\tNOT SET (default " << this->m_Parameter->SampleWithReplacement << ")" << "\n";
else
str << "samplewithreplacement\t" << this->m_Parameter->SampleWithReplacement << "\n";
if(!this->GetPropertyList()->Get("treecount",this->m_Parameter->TreeCount))
str << "treecount\t\tNOT SET (default " << this->m_Parameter->TreeCount << ")" << "\n";
else
str << "treecount\t\t" << this->m_Parameter->TreeCount << "\n";
if(!this->GetPropertyList()->Get("lambda",this->m_Parameter->WeightLambda))
str << "lambda\t\tNOT SET (default " << this->m_Parameter->WeightLambda << ")" << "\n";
else
str << "lambda\t\t" << this->m_Parameter->WeightLambda << "\n";
// if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->Stratification))
// this->m_Parameter->Stratification = vigra:RF_NONE; // no Property given
}
void mitk::PURFClassifier::UsePointWiseWeight(bool val)
{
mitk::AbstractClassifier::UsePointWiseWeight(val);
this->GetPropertyList()->SetBoolProperty("usepointbasedweight",val);
}
void mitk::PURFClassifier::SetMaximumTreeDepth(int val)
{
this->GetPropertyList()->SetIntProperty("treedepth",val);
}
void mitk::PURFClassifier::SetMinimumSplitNodeSize(int val)
{
this->GetPropertyList()->SetIntProperty("minimalsplitnodesize",val);
}
void mitk::PURFClassifier::SetPrecision(double val)
{
this->GetPropertyList()->SetDoubleProperty("precision",val);
}
void mitk::PURFClassifier::SetSamplesPerTree(double val)
{
this->GetPropertyList()->SetDoubleProperty("samplespertree",val);
}
void mitk::PURFClassifier::UseSampleWithReplacement(bool val)
{
this->GetPropertyList()->SetBoolProperty("samplewithreplacement",val);
}
void mitk::PURFClassifier::SetTreeCount(int val)
{
this->GetPropertyList()->SetIntProperty("treecount",val);
}
void mitk::PURFClassifier::SetWeightLambda(double val)
{
this->GetPropertyList()->SetDoubleProperty("lambda",val);
}
void mitk::PURFClassifier::SetRandomForest(const vigra::RandomForest<int> & rf)
{
this->SetMaximumTreeDepth(rf.ext_param().max_tree_depth);
this->SetMinimumSplitNodeSize(rf.options().min_split_node_size_);
this->SetTreeCount(rf.options().tree_count_);
this->SetSamplesPerTree(rf.options().training_set_proportion_);
this->UseSampleWithReplacement(rf.options().sample_with_replacement_);
this->m_RandomForest = rf;
}
const vigra::RandomForest<int> & mitk::PURFClassifier::GetRandomForest() const
{
return this->m_RandomForest;
}
diff --git a/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkVigraRandomForestClassifier.cpp b/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkVigraRandomForestClassifier.cpp
index 95d0295d2e..840d58d270 100644
--- a/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkVigraRandomForestClassifier.cpp
+++ b/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkVigraRandomForestClassifier.cpp
@@ -1,589 +1,589 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// MITK includes
#include <mitkVigraRandomForestClassifier.h>
#include <mitkThresholdSplit.h>
#include <mitkImpurityLoss.h>
#include <mitkLinearSplitting.h>
#include <mitkProperties.h>
// Vigra includes
#include <vigra/random_forest.hxx>
#include <vigra/random_forest/rf_split.hxx>
// ITK include
-#include <itkFastMutexLock.h>
-#include <itkMultiThreader.h>
+#include <itkMultiThreaderBase.h>
#include <itkCommand.h>
+#include <mutex>
+
typedef mitk::ThresholdSplit<mitk::LinearSplitting< mitk::ImpurityLoss<> >,int,vigra::ClassificationTag> DefaultSplitType;
struct mitk::VigraRandomForestClassifier::Parameter
{
vigra::RF_OptionTag Stratification;
bool SampleWithReplacement;
bool UseRandomSplit;
bool UsePointBasedWeights;
int TreeCount;
int MinimumSplitNodeSize;
int TreeDepth;
double Precision;
double WeightLambda;
double SamplesPerTree;
};
struct mitk::VigraRandomForestClassifier::TrainingData
{
TrainingData(unsigned int numberOfTrees,
const vigra::RandomForest<int> & refRF,
const DefaultSplitType & refSplitter,
const vigra::MultiArrayView<2, double> refFeature,
const vigra::MultiArrayView<2, int> refLabel,
const Parameter parameter)
: m_ClassCount(0),
m_NumberOfTrees(numberOfTrees),
m_RandomForest(refRF),
m_Splitter(refSplitter),
m_Feature(refFeature),
m_Label(refLabel),
m_Parameter(parameter)
{
- m_mutex = itk::FastMutexLock::New();
}
vigra::ArrayVector<vigra::RandomForest<int>::DecisionTree_t> trees_;
int m_ClassCount;
unsigned int m_NumberOfTrees;
const vigra::RandomForest<int> & m_RandomForest;
const DefaultSplitType & m_Splitter;
const vigra::MultiArrayView<2, double> m_Feature;
const vigra::MultiArrayView<2, int> m_Label;
- itk::FastMutexLock::Pointer m_mutex;
+ std::mutex m_mutex;
Parameter m_Parameter;
};
struct mitk::VigraRandomForestClassifier::PredictionData
{
PredictionData(const vigra::RandomForest<int> & refRF,
const vigra::MultiArrayView<2, double> refFeature,
vigra::MultiArrayView<2, int> refLabel,
vigra::MultiArrayView<2, double> refProb,
vigra::MultiArrayView<2, double> refTreeWeights)
: m_RandomForest(refRF),
m_Feature(refFeature),
m_Label(refLabel),
m_Probabilities(refProb),
m_TreeWeights(refTreeWeights)
{
}
const vigra::RandomForest<int> & m_RandomForest;
const vigra::MultiArrayView<2, double> m_Feature;
vigra::MultiArrayView<2, int> m_Label;
vigra::MultiArrayView<2, double> m_Probabilities;
vigra::MultiArrayView<2, double> m_TreeWeights;
};
mitk::VigraRandomForestClassifier::VigraRandomForestClassifier()
:m_Parameter(nullptr)
{
itk::SimpleMemberCommand<mitk::VigraRandomForestClassifier>::Pointer command = itk::SimpleMemberCommand<mitk::VigraRandomForestClassifier>::New();
command->SetCallbackFunction(this, &mitk::VigraRandomForestClassifier::ConvertParameter);
this->GetPropertyList()->AddObserver( itk::ModifiedEvent(), command );
}
mitk::VigraRandomForestClassifier::~VigraRandomForestClassifier()
{
}
bool mitk::VigraRandomForestClassifier::SupportsPointWiseWeight()
{
return true;
}
bool mitk::VigraRandomForestClassifier::SupportsPointWiseProbability()
{
return true;
}
void mitk::VigraRandomForestClassifier::OnlineTrain(const Eigen::MatrixXd & X_in, const Eigen::MatrixXi &Y_in)
{
vigra::MultiArrayView<2, double> X(vigra::Shape2(X_in.rows(),X_in.cols()),X_in.data());
vigra::MultiArrayView<2, int> Y(vigra::Shape2(Y_in.rows(),Y_in.cols()),Y_in.data());
m_RandomForest.onlineLearn(X,Y,0,true);
}
void mitk::VigraRandomForestClassifier::Train(const Eigen::MatrixXd & X_in, const Eigen::MatrixXi &Y_in)
{
this->ConvertParameter();
DefaultSplitType splitter;
splitter.UsePointBasedWeights(m_Parameter->UsePointBasedWeights);
splitter.UseRandomSplit(m_Parameter->UseRandomSplit);
splitter.SetPrecision(m_Parameter->Precision);
splitter.SetMaximumTreeDepth(m_Parameter->TreeDepth);
// Weights handled as member variable
if (m_Parameter->UsePointBasedWeights)
{
// Set influence of the weight (0 no influenc to 1 max influence)
this->m_PointWiseWeight.unaryExpr([this](double t){ return std::pow(t, this->m_Parameter->WeightLambda) ;});
vigra::MultiArrayView<2, double> W(vigra::Shape2(this->m_PointWiseWeight.rows(),this->m_PointWiseWeight.cols()),this->m_PointWiseWeight.data());
splitter.SetWeights(W);
}
vigra::MultiArrayView<2, double> X(vigra::Shape2(X_in.rows(),X_in.cols()),X_in.data());
vigra::MultiArrayView<2, int> Y(vigra::Shape2(Y_in.rows(),Y_in.cols()),Y_in.data());
m_RandomForest.set_options().tree_count(1); // Number of trees that are calculated;
m_RandomForest.set_options().use_stratification(m_Parameter->Stratification);
m_RandomForest.set_options().sample_with_replacement(m_Parameter->SampleWithReplacement);
m_RandomForest.set_options().samples_per_tree(m_Parameter->SamplesPerTree);
m_RandomForest.set_options().min_split_node_size(m_Parameter->MinimumSplitNodeSize);
m_RandomForest.learn(X, Y,vigra::rf::visitors::VisitorBase(),splitter);
std::unique_ptr<TrainingData> data(new TrainingData(m_Parameter->TreeCount,m_RandomForest,splitter,X,Y, *m_Parameter));
- itk::MultiThreader::Pointer threader = itk::MultiThreader::New();
+ auto threader = itk::MultiThreaderBase::New();
threader->SetSingleMethod(this->TrainTreesCallback,data.get());
threader->SingleMethodExecute();
// set result trees
m_RandomForest.set_options().tree_count(m_Parameter->TreeCount);
m_RandomForest.ext_param_.class_count_ = data->m_ClassCount;
m_RandomForest.trees_ = data->trees_;
// Set Tree Weights to default
m_TreeWeights = Eigen::MatrixXd(m_Parameter->TreeCount,1);
m_TreeWeights.fill(1.0);
}
Eigen::MatrixXi mitk::VigraRandomForestClassifier::Predict(const Eigen::MatrixXd &X_in)
{
// Initialize output Eigen matrices
m_OutProbability = Eigen::MatrixXd(X_in.rows(),m_RandomForest.class_count());
m_OutProbability.fill(0);
m_OutLabel = Eigen::MatrixXi(X_in.rows(),1);
m_OutLabel.fill(0);
// If no weights provided
if(m_TreeWeights.rows() != m_RandomForest.tree_count())
{
m_TreeWeights = Eigen::MatrixXd(m_RandomForest.tree_count(),1);
m_TreeWeights.fill(1);
}
vigra::MultiArrayView<2, double> P(vigra::Shape2(m_OutProbability.rows(),m_OutProbability.cols()),m_OutProbability.data());
vigra::MultiArrayView<2, int> Y(vigra::Shape2(m_OutLabel.rows(),m_OutLabel.cols()),m_OutLabel.data());
vigra::MultiArrayView<2, double> X(vigra::Shape2(X_in.rows(),X_in.cols()),X_in.data());
vigra::MultiArrayView<2, double> TW(vigra::Shape2(m_RandomForest.tree_count(),1),m_TreeWeights.data());
std::unique_ptr<PredictionData> data;
data.reset(new PredictionData(m_RandomForest, X, Y, P, TW));
- itk::MultiThreader::Pointer threader = itk::MultiThreader::New();
+ auto threader = itk::MultiThreaderBase::New();
threader->SetSingleMethod(this->PredictCallback, data.get());
threader->SingleMethodExecute();
m_Probabilities = data->m_Probabilities;
return m_OutLabel;
}
Eigen::MatrixXi mitk::VigraRandomForestClassifier::PredictWeighted(const Eigen::MatrixXd &X_in)
{
// Initialize output Eigen matrices
m_OutProbability = Eigen::MatrixXd(X_in.rows(),m_RandomForest.class_count());
m_OutProbability.fill(0);
m_OutLabel = Eigen::MatrixXi(X_in.rows(),1);
m_OutLabel.fill(0);
// If no weights provided
if(m_TreeWeights.rows() != m_RandomForest.tree_count())
{
m_TreeWeights = Eigen::MatrixXd(m_RandomForest.tree_count(),1);
m_TreeWeights.fill(1);
}
vigra::MultiArrayView<2, double> P(vigra::Shape2(m_OutProbability.rows(),m_OutProbability.cols()),m_OutProbability.data());
vigra::MultiArrayView<2, int> Y(vigra::Shape2(m_OutLabel.rows(),m_OutLabel.cols()),m_OutLabel.data());
vigra::MultiArrayView<2, double> X(vigra::Shape2(X_in.rows(),X_in.cols()),X_in.data());
vigra::MultiArrayView<2, double> TW(vigra::Shape2(m_RandomForest.tree_count(),1),m_TreeWeights.data());
std::unique_ptr<PredictionData> data;
data.reset( new PredictionData(m_RandomForest,X,Y,P,TW));
- itk::MultiThreader::Pointer threader = itk::MultiThreader::New();
+ auto threader = itk::MultiThreaderBase::New();
threader->SetSingleMethod(this->PredictWeightedCallback,data.get());
threader->SingleMethodExecute();
return m_OutLabel;
}
void mitk::VigraRandomForestClassifier::SetTreeWeights(Eigen::MatrixXd weights)
{
m_TreeWeights = weights;
}
Eigen::MatrixXd mitk::VigraRandomForestClassifier::GetTreeWeights() const
{
return m_TreeWeights;
}
-ITK_THREAD_RETURN_TYPE mitk::VigraRandomForestClassifier::TrainTreesCallback(void * arg)
+itk::ITK_THREAD_RETURN_TYPE mitk::VigraRandomForestClassifier::TrainTreesCallback(void * arg)
{
// Get the ThreadInfoStruct
- typedef itk::MultiThreader::ThreadInfoStruct ThreadInfoType;
+ typedef itk::MultiThreaderBase::WorkUnitInfo ThreadInfoType;
ThreadInfoType * infoStruct = static_cast< ThreadInfoType * >( arg );
TrainingData * data = (TrainingData *)(infoStruct->UserData);
unsigned int numberOfTreesToCalculate = 0;
// define the number of tress the forest have to calculate
- numberOfTreesToCalculate = data->m_NumberOfTrees / infoStruct->NumberOfThreads;
+ numberOfTreesToCalculate = data->m_NumberOfTrees / infoStruct->NumberOfWorkUnits;
// the 0th thread takes the residuals
- if(infoStruct->ThreadID == 0) numberOfTreesToCalculate += data->m_NumberOfTrees % infoStruct->NumberOfThreads;
+ if(infoStruct->WorkUnitID == 0) numberOfTreesToCalculate += data->m_NumberOfTrees % infoStruct->NumberOfWorkUnits;
if(numberOfTreesToCalculate != 0){
// Copy the Treestructure defined in userData
vigra::RandomForest<int> rf = data->m_RandomForest;
// Initialize a splitter for the leraning process
DefaultSplitType splitter;
splitter.UsePointBasedWeights(data->m_Splitter.IsUsingPointBasedWeights());
splitter.UseRandomSplit(data->m_Splitter.IsUsingRandomSplit());
splitter.SetPrecision(data->m_Splitter.GetPrecision());
splitter.SetMaximumTreeDepth(data->m_Splitter.GetMaximumTreeDepth());
splitter.SetWeights(data->m_Splitter.GetWeights());
rf.trees_.clear();
rf.set_options().tree_count(numberOfTreesToCalculate);
rf.set_options().use_stratification(data->m_Parameter.Stratification);
rf.set_options().sample_with_replacement(data->m_Parameter.SampleWithReplacement);
rf.set_options().samples_per_tree(data->m_Parameter.SamplesPerTree);
rf.set_options().min_split_node_size(data->m_Parameter.MinimumSplitNodeSize);
rf.learn(data->m_Feature, data->m_Label,vigra::rf::visitors::VisitorBase(),splitter);
- data->m_mutex->Lock();
+ data->m_mutex.lock();
for(const auto & tree : rf.trees_)
data->trees_.push_back(tree);
data->m_ClassCount = rf.class_count();
- data->m_mutex->Unlock();
+ data->m_mutex.unlock();
}
- return ITK_THREAD_RETURN_VALUE;
+ return ITK_THREAD_RETURN_DEFAULT_VALUE;
}
-ITK_THREAD_RETURN_TYPE mitk::VigraRandomForestClassifier::PredictCallback(void * arg)
+itk::ITK_THREAD_RETURN_TYPE mitk::VigraRandomForestClassifier::PredictCallback(void * arg)
{
// Get the ThreadInfoStruct
- typedef itk::MultiThreader::ThreadInfoStruct ThreadInfoType;
+ typedef itk::MultiThreaderBase::WorkUnitInfo ThreadInfoType;
ThreadInfoType * infoStruct = static_cast< ThreadInfoType * >( arg );
// assigne the thread id
- const unsigned int threadId = infoStruct->ThreadID;
+ const unsigned int threadId = infoStruct->WorkUnitID;
// Get the user defined parameters containing all
// neccesary informations
PredictionData * data = (PredictionData *)(infoStruct->UserData);
unsigned int numberOfRowsToCalculate = 0;
// Get number of rows to calculate
- numberOfRowsToCalculate = data->m_Feature.shape()[0] / infoStruct->NumberOfThreads;
+ numberOfRowsToCalculate = data->m_Feature.shape()[0] / infoStruct->NumberOfWorkUnits;
unsigned int start_index = numberOfRowsToCalculate * threadId;
unsigned int end_index = numberOfRowsToCalculate * (threadId+1);
// the last thread takes the residuals
- if(threadId == infoStruct->NumberOfThreads-1) {
- end_index += data->m_Feature.shape()[0] % infoStruct->NumberOfThreads;
+ if(threadId == infoStruct->NumberOfWorkUnits-1) {
+ end_index += data->m_Feature.shape()[0] % infoStruct->NumberOfWorkUnits;
}
vigra::MultiArrayView<2, double> split_features;
vigra::MultiArrayView<2, int> split_labels;
vigra::MultiArrayView<2, double> split_probability;
{
vigra::TinyVector<vigra::MultiArrayIndex, 2> lowerBound(start_index,0);
vigra::TinyVector<vigra::MultiArrayIndex, 2> upperBound(end_index,data->m_Feature.shape(1));
split_features = data->m_Feature.subarray(lowerBound,upperBound);
}
{
vigra::TinyVector<vigra::MultiArrayIndex, 2> lowerBound(start_index,0);
vigra::TinyVector<vigra::MultiArrayIndex, 2> upperBound(end_index, data->m_Label.shape(1));
split_labels = data->m_Label.subarray(lowerBound,upperBound);
}
{
vigra::TinyVector<vigra::MultiArrayIndex, 2> lowerBound(start_index,0);
vigra::TinyVector<vigra::MultiArrayIndex, 2> upperBound(end_index,data->m_Probabilities.shape(1));
split_probability = data->m_Probabilities.subarray(lowerBound,upperBound);
}
data->m_RandomForest.predictLabels(split_features,split_labels);
data->m_RandomForest.predictProbabilities(split_features, split_probability);
- return ITK_THREAD_RETURN_VALUE;
+ return ITK_THREAD_RETURN_DEFAULT_VALUE;
}
-ITK_THREAD_RETURN_TYPE mitk::VigraRandomForestClassifier::PredictWeightedCallback(void * arg)
+itk::ITK_THREAD_RETURN_TYPE mitk::VigraRandomForestClassifier::PredictWeightedCallback(void * arg)
{
// Get the ThreadInfoStruct
- typedef itk::MultiThreader::ThreadInfoStruct ThreadInfoType;
+ typedef itk::MultiThreaderBase::WorkUnitInfo ThreadInfoType;
ThreadInfoType * infoStruct = static_cast< ThreadInfoType * >( arg );
// assigne the thread id
- const unsigned int threadId = infoStruct->ThreadID;
+ const unsigned int threadId = infoStruct->WorkUnitID;
// Get the user defined parameters containing all
// neccesary informations
PredictionData * data = (PredictionData *)(infoStruct->UserData);
unsigned int numberOfRowsToCalculate = 0;
// Get number of rows to calculate
- numberOfRowsToCalculate = data->m_Feature.shape()[0] / infoStruct->NumberOfThreads;
+ numberOfRowsToCalculate = data->m_Feature.shape()[0] / infoStruct->NumberOfWorkUnits;
unsigned int start_index = numberOfRowsToCalculate * threadId;
unsigned int end_index = numberOfRowsToCalculate * (threadId+1);
// the last thread takes the residuals
- if(threadId == infoStruct->NumberOfThreads-1) {
- end_index += data->m_Feature.shape()[0] % infoStruct->NumberOfThreads;
+ if(threadId == infoStruct->NumberOfWorkUnits-1) {
+ end_index += data->m_Feature.shape()[0] % infoStruct->NumberOfWorkUnits;
}
vigra::MultiArrayView<2, double> split_features;
vigra::MultiArrayView<2, int> split_labels;
vigra::MultiArrayView<2, double> split_probability;
{
vigra::TinyVector<vigra::MultiArrayIndex, 2> lowerBound(start_index,0);
vigra::TinyVector<vigra::MultiArrayIndex, 2> upperBound(end_index,data->m_Feature.shape(1));
split_features = data->m_Feature.subarray(lowerBound,upperBound);
}
{
vigra::TinyVector<vigra::MultiArrayIndex, 2> lowerBound(start_index,0);
vigra::TinyVector<vigra::MultiArrayIndex, 2> upperBound(end_index, data->m_Label.shape(1));
split_labels = data->m_Label.subarray(lowerBound,upperBound);
}
{
vigra::TinyVector<vigra::MultiArrayIndex, 2> lowerBound(start_index,0);
vigra::TinyVector<vigra::MultiArrayIndex, 2> upperBound(end_index,data->m_Probabilities.shape(1));
split_probability = data->m_Probabilities.subarray(lowerBound,upperBound);
}
VigraPredictWeighted(data, split_features,split_labels,split_probability);
- return ITK_THREAD_RETURN_VALUE;
+ return ITK_THREAD_RETURN_DEFAULT_VALUE;
}
void mitk::VigraRandomForestClassifier::VigraPredictWeighted(PredictionData * data, vigra::MultiArrayView<2, double> & X, vigra::MultiArrayView<2, int> & Y, vigra::MultiArrayView<2, double> & P)
{
int isSampleWeighted = data->m_RandomForest.options_.predict_weighted_;
//#pragma omp parallel for
for(int row=0; row < vigra::rowCount(X); ++row)
{
vigra::MultiArrayView<2, double, vigra::StridedArrayTag> currentRow(rowVector(X, row));
vigra::ArrayVector<double>::const_iterator weights;
//totalWeight == totalVoteCount!
double totalWeight = 0.0;
//Let each tree classify...
for(int k=0; k<data->m_RandomForest.options_.tree_count_; ++k)
{
//get weights predicted by single tree
weights = data->m_RandomForest.trees_[k /*tree_indices_[k]*/].predict(currentRow);
double numberOfLeafObservations = (*(weights-1));
//update votecount.
for(int l=0; l<data->m_RandomForest.ext_param_.class_count_; ++l)
{
// Either the original weights are taken or the tree is additional weighted by the number of Observations in the leaf node.
double cur_w = weights[l] * (isSampleWeighted * numberOfLeafObservations + (1-isSampleWeighted));
cur_w = cur_w * data->m_TreeWeights(k,0);
P(row, l) += (int)cur_w;
//every weight in totalWeight.
totalWeight += cur_w;
}
}
//Normalise votes in each row by total VoteCount (totalWeight
for(int l=0; l< data->m_RandomForest.ext_param_.class_count_; ++l)
{
P(row, l) /= vigra::detail::RequiresExplicitCast<double>::cast(totalWeight);
}
int erg;
int maxCol = 0;
for (int col=0;col<data->m_RandomForest.class_count();++col)
{
if (data->m_Probabilities(row,col) > data->m_Probabilities(row, maxCol))
maxCol = col;
}
data->m_RandomForest.ext_param_.to_classlabel(maxCol, erg);
Y(row,0) = erg;
}
}
void mitk::VigraRandomForestClassifier::ConvertParameter()
{
if(this->m_Parameter == nullptr)
this->m_Parameter = new Parameter();
// Get the proerty // Some defaults
MITK_INFO("VigraRandomForestClassifier") << "Convert Parameter";
if(!this->GetPropertyList()->Get("usepointbasedweight",this->m_Parameter->UsePointBasedWeights)) this->m_Parameter->UsePointBasedWeights = false;
if(!this->GetPropertyList()->Get("userandomsplit",this->m_Parameter->UseRandomSplit)) this->m_Parameter->UseRandomSplit = false;
if(!this->GetPropertyList()->Get("treedepth",this->m_Parameter->TreeDepth)) this->m_Parameter->TreeDepth = 20;
if(!this->GetPropertyList()->Get("treecount",this->m_Parameter->TreeCount)) this->m_Parameter->TreeCount = 100;
if(!this->GetPropertyList()->Get("minimalsplitnodesize",this->m_Parameter->MinimumSplitNodeSize)) this->m_Parameter->MinimumSplitNodeSize = 5;
if(!this->GetPropertyList()->Get("precision",this->m_Parameter->Precision)) this->m_Parameter->Precision = mitk::eps;
if(!this->GetPropertyList()->Get("samplespertree",this->m_Parameter->SamplesPerTree)) this->m_Parameter->SamplesPerTree = 0.6;
if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->SampleWithReplacement)) this->m_Parameter->SampleWithReplacement = true;
if(!this->GetPropertyList()->Get("lambda",this->m_Parameter->WeightLambda)) this->m_Parameter->WeightLambda = 1.0; // Not used yet
// if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->Stratification))
this->m_Parameter->Stratification = vigra::RF_NONE; // no Property given
}
void mitk::VigraRandomForestClassifier::PrintParameter(std::ostream & str)
{
if(this->m_Parameter == nullptr)
{
MITK_WARN("VigraRandomForestClassifier") << "Parameters are not initialized. Please call ConvertParameter() first!";
return;
}
this->ConvertParameter();
// Get the proerty // Some defaults
if(!this->GetPropertyList()->Get("usepointbasedweight",this->m_Parameter->UsePointBasedWeights))
str << "usepointbasedweight\tNOT SET (default " << this->m_Parameter->UsePointBasedWeights << ")" << "\n";
else
str << "usepointbasedweight\t" << this->m_Parameter->UsePointBasedWeights << "\n";
if(!this->GetPropertyList()->Get("userandomsplit",this->m_Parameter->UseRandomSplit))
str << "userandomsplit\tNOT SET (default " << this->m_Parameter->UseRandomSplit << ")" << "\n";
else
str << "userandomsplit\t" << this->m_Parameter->UseRandomSplit << "\n";
if(!this->GetPropertyList()->Get("treedepth",this->m_Parameter->TreeDepth))
str << "treedepth\t\tNOT SET (default " << this->m_Parameter->TreeDepth << ")" << "\n";
else
str << "treedepth\t\t" << this->m_Parameter->TreeDepth << "\n";
if(!this->GetPropertyList()->Get("minimalsplitnodesize",this->m_Parameter->MinimumSplitNodeSize))
str << "minimalsplitnodesize\tNOT SET (default " << this->m_Parameter->MinimumSplitNodeSize << ")" << "\n";
else
str << "minimalsplitnodesize\t" << this->m_Parameter->MinimumSplitNodeSize << "\n";
if(!this->GetPropertyList()->Get("precision",this->m_Parameter->Precision))
str << "precision\t\tNOT SET (default " << this->m_Parameter->Precision << ")" << "\n";
else
str << "precision\t\t" << this->m_Parameter->Precision << "\n";
if(!this->GetPropertyList()->Get("samplespertree",this->m_Parameter->SamplesPerTree))
str << "samplespertree\tNOT SET (default " << this->m_Parameter->SamplesPerTree << ")" << "\n";
else
str << "samplespertree\t" << this->m_Parameter->SamplesPerTree << "\n";
if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->SampleWithReplacement))
str << "samplewithreplacement\tNOT SET (default " << this->m_Parameter->SampleWithReplacement << ")" << "\n";
else
str << "samplewithreplacement\t" << this->m_Parameter->SampleWithReplacement << "\n";
if(!this->GetPropertyList()->Get("treecount",this->m_Parameter->TreeCount))
str << "treecount\t\tNOT SET (default " << this->m_Parameter->TreeCount << ")" << "\n";
else
str << "treecount\t\t" << this->m_Parameter->TreeCount << "\n";
if(!this->GetPropertyList()->Get("lambda",this->m_Parameter->WeightLambda))
str << "lambda\t\tNOT SET (default " << this->m_Parameter->WeightLambda << ")" << "\n";
else
str << "lambda\t\t" << this->m_Parameter->WeightLambda << "\n";
// if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->Stratification))
// this->m_Parameter->Stratification = vigra:RF_NONE; // no Property given
}
void mitk::VigraRandomForestClassifier::UsePointWiseWeight(bool val)
{
mitk::AbstractClassifier::UsePointWiseWeight(val);
this->GetPropertyList()->SetBoolProperty("usepointbasedweight",val);
}
void mitk::VigraRandomForestClassifier::SetMaximumTreeDepth(int val)
{
this->GetPropertyList()->SetIntProperty("treedepth",val);
}
void mitk::VigraRandomForestClassifier::SetMinimumSplitNodeSize(int val)
{
this->GetPropertyList()->SetIntProperty("minimalsplitnodesize",val);
}
void mitk::VigraRandomForestClassifier::SetPrecision(double val)
{
this->GetPropertyList()->SetDoubleProperty("precision",val);
}
void mitk::VigraRandomForestClassifier::SetSamplesPerTree(double val)
{
this->GetPropertyList()->SetDoubleProperty("samplespertree",val);
}
void mitk::VigraRandomForestClassifier::UseSampleWithReplacement(bool val)
{
this->GetPropertyList()->SetBoolProperty("samplewithreplacement",val);
}
void mitk::VigraRandomForestClassifier::SetTreeCount(int val)
{
this->GetPropertyList()->SetIntProperty("treecount",val);
}
void mitk::VigraRandomForestClassifier::SetWeightLambda(double val)
{
this->GetPropertyList()->SetDoubleProperty("lambda",val);
}
void mitk::VigraRandomForestClassifier::SetTreeWeight(int treeId, double weight)
{
m_TreeWeights(treeId,0) = weight;
}
void mitk::VigraRandomForestClassifier::SetRandomForest(const vigra::RandomForest<int> & rf)
{
this->SetMaximumTreeDepth(rf.ext_param().max_tree_depth);
this->SetMinimumSplitNodeSize(rf.options().min_split_node_size_);
this->SetTreeCount(rf.options().tree_count_);
this->SetSamplesPerTree(rf.options().training_set_proportion_);
this->UseSampleWithReplacement(rf.options().sample_with_replacement_);
this->m_RandomForest = rf;
}
const vigra::RandomForest<int> & mitk::VigraRandomForestClassifier::GetRandomForest() const
{
return this->m_RandomForest;
}
diff --git a/Modules/ContourModel/DataManagement/mitkContourElement.cpp b/Modules/ContourModel/DataManagement/mitkContourElement.cpp
index be73ced97b..02cfc0c4a3 100644
--- a/Modules/ContourModel/DataManagement/mitkContourElement.cpp
+++ b/Modules/ContourModel/DataManagement/mitkContourElement.cpp
@@ -1,489 +1,489 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <algorithm>
#include <mitkContourElement.h>
#include <vtkMath.h>
bool mitk::ContourElement::ContourModelVertex::operator==(const ContourModelVertex &other) const
{
return this->Coordinates == other.Coordinates && this->IsControlPoint == other.IsControlPoint;
}
mitk::ContourElement::ConstVertexIterator mitk::ContourElement::ConstIteratorBegin() const
{
return this->begin();
}
mitk::ContourElement::ConstVertexIterator mitk::ContourElement::ConstIteratorEnd() const
{
return this->end();
}
mitk::ContourElement::VertexIterator mitk::ContourElement::IteratorBegin()
{
return this->begin();
}
mitk::ContourElement::VertexIterator mitk::ContourElement::IteratorEnd()
{
return this->end();
}
mitk::ContourElement::ConstVertexIterator mitk::ContourElement::begin() const
{
return this->m_Vertices.begin();
}
mitk::ContourElement::ConstVertexIterator mitk::ContourElement::end() const
{
return this->m_Vertices.end();
}
mitk::ContourElement::VertexIterator mitk::ContourElement::begin()
{
return this->m_Vertices.begin();
}
mitk::ContourElement::VertexIterator mitk::ContourElement::end()
{
return this->m_Vertices.end();
}
mitk::ContourElement::ContourElement(const mitk::ContourElement &other)
: itk::LightObject(), m_IsClosed(other.m_IsClosed)
{
for (const auto &v : other.m_Vertices)
{
m_Vertices.push_back(new ContourModelVertex(*v));
}
}
mitk::ContourElement &mitk::ContourElement::operator=(const ContourElement &other)
{
if (this != &other)
{
this->Clear();
for (const auto &v : other.m_Vertices)
{
m_Vertices.push_back(new ContourModelVertex(*v));
}
}
this->m_IsClosed = other.m_IsClosed;
return *this;
}
mitk::ContourElement::~ContourElement()
{
this->Clear();
}
mitk::ContourElement::VertexSizeType mitk::ContourElement::GetSize() const
{
return this->m_Vertices.size();
}
void mitk::ContourElement::AddVertex(const mitk::Point3D &vertex, bool isControlPoint)
{
this->m_Vertices.push_back(new VertexType(vertex, isControlPoint));
}
void mitk::ContourElement::AddVertexAtFront(const mitk::Point3D &vertex, bool isControlPoint)
{
this->m_Vertices.push_front(new VertexType(vertex, isControlPoint));
}
void mitk::ContourElement::InsertVertexAtIndex(const mitk::Point3D &vertex, bool isControlPoint, VertexSizeType index)
{
- if (index >= 0 && this->GetSize() > index)
+ if (this->GetSize() > index)
{
auto _where = this->m_Vertices.begin();
_where += index;
this->m_Vertices.insert(_where, new VertexType(vertex, isControlPoint));
}
}
void mitk::ContourElement::SetVertexAt(VertexSizeType pointId, const Point3D &point)
{
- if (pointId >= 0 && this->GetSize() > pointId)
+ if (this->GetSize() > pointId)
{
this->m_Vertices[pointId]->Coordinates = point;
}
}
void mitk::ContourElement::SetVertexAt(VertexSizeType pointId, const VertexType *vertex)
{
if (nullptr == vertex)
{
mitkThrow() << "Cannot set vertex. Passed vertex instance is invalid. Index to set: " << pointId;
}
- if (pointId >= 0 && this->GetSize() > pointId)
+ if (this->GetSize() > pointId)
{
this->m_Vertices[pointId]->Coordinates = vertex->Coordinates;
this->m_Vertices[pointId]->IsControlPoint = vertex->IsControlPoint;
}
}
mitk::ContourElement::VertexType *mitk::ContourElement::GetVertexAt(VertexSizeType index)
{
return this->m_Vertices.at(index);
}
const mitk::ContourElement::VertexType *mitk::ContourElement::GetVertexAt(VertexSizeType index) const
{
return this->m_Vertices.at(index);
}
bool mitk::ContourElement::IsEmpty() const
{
return this->m_Vertices.empty();
}
mitk::ContourElement::VertexType *mitk::ContourElement::GetControlVertexAt(const mitk::Point3D &point, float eps)
{
/* current version iterates over the whole deque - should some kind of an octree with spatial query*/
if (eps > 0)
{
// currently no method with better performance is available
return BruteForceGetVertexAt(point, eps, true);
} // if eps < 0
return nullptr;
}
mitk::ContourElement::VertexType *mitk::ContourElement::GetVertexAt(const mitk::Point3D &point, float eps)
{
/* current version iterates over the whole deque - should some kind of an octree with spatial query*/
if (eps > 0)
{
// currently no method with better performance is available
return BruteForceGetVertexAt(point, eps);
} // if eps < 0
return nullptr;
}
mitk::ContourElement::VertexType *mitk::ContourElement::GetNextControlVertexAt(const mitk::Point3D &point, float eps)
{
/* current version iterates over the whole deque - should some kind of an octree with spatial query*/
if (eps > 0)
{
// currently no method with better performance is available
return BruteForceGetVertexAt(point, eps, true, 1);
} // if eps < 0
return nullptr;
}
mitk::ContourElement::VertexType *mitk::ContourElement::GetPreviousControlVertexAt(const mitk::Point3D &point, float eps)
{
/* current version iterates over the whole deque - should some kind of an octree with spatial query*/
if (eps > 0)
{
// currently no method with better performance is available
return BruteForceGetVertexAt(point, eps, true, -1);
} // if eps < 0
return nullptr;
}
mitk::ContourElement::VertexType *mitk::ContourElement::BruteForceGetVertexAt(const mitk::Point3D &point,
double eps,
bool isControlPoint,
int offset)
{
VertexListType verticesList;
if (isControlPoint)
{
verticesList = this->GetControlVertices();
}
else
{
verticesList = *this->GetVertexList();
}
int vertexIndex = BruteForceGetVertexIndexAt(point, eps, verticesList);
if (vertexIndex!=-1)
{
vertexIndex += offset;
if (vertexIndex < 0)
{
// for negative offset
// if the offset exceeds the first vertex, we start from the end of the vertex list backwards
vertexIndex = verticesList.size() + offset;
}
else if (vertexIndex >= (int) verticesList.size())
{
// if the offset exceeds the last vertex, we start from the beginning of the vertex list
vertexIndex = vertexIndex - verticesList.size();
}
return verticesList[vertexIndex];
}
return nullptr;
}
int mitk::ContourElement::BruteForceGetVertexIndexAt(const mitk::Point3D &point,
double eps,
VertexListType verticesList)
{
if (eps < 0)
{
mitkThrow() << "Distance cannot be negative";
}
ConstVertexIterator nearestPointIterator;
bool nearestPointIsInitialized = false;
double nearestPointDistance = std::numeric_limits<double>::max();
ConstVertexIterator it = verticesList.begin();
ConstVertexIterator end = verticesList.end();
while (it != end)
{
mitk::Point3D currentPoint = (*it)->Coordinates;
double distance = currentPoint.EuclideanDistanceTo(point);
if (distance < eps)
{
if (distance < nearestPointDistance)
{
nearestPointIterator = it;
nearestPointIsInitialized = true;
nearestPointDistance = distance;
}
} // if distance > eps
it++;
} // while
if (nearestPointIsInitialized)
{
return nearestPointIterator - verticesList.begin();
}
return -1;
}
const mitk::ContourElement::VertexListType *mitk::ContourElement::GetVertexList() const
{
return &(this->m_Vertices);
}
bool mitk::ContourElement::IsClosed() const
{
return this->m_IsClosed;
}
bool mitk::ContourElement::IsNearContour(const mitk::Point3D &point, float eps) const
{
ConstVertexIterator it1 = this->m_Vertices.begin();
ConstVertexIterator it2 = this->m_Vertices.begin();
it2++; // it2 runs one position ahead
ConstVertexIterator end = this->m_Vertices.end();
int counter = 0;
for (; it1 != end; it1++, it2++, counter++)
{
if (it2 == end)
it2 = this->m_Vertices.begin();
mitk::Point3D v1 = (*it1)->Coordinates;
mitk::Point3D v2 = (*it2)->Coordinates;
const float l2 = v1.SquaredEuclideanDistanceTo(v2);
mitk::Vector3D p_v1 = point - v1;
mitk::Vector3D v2_v1 = v2 - v1;
double tc = (p_v1 * v2_v1) / l2;
// take into account we have line segments and not (infinite) lines
if (tc < 0.0)
tc = 0.0;
if (tc > 1.0)
tc = 1.0;
mitk::Point3D crossPoint = v1 + v2_v1 * tc;
double distance = point.SquaredEuclideanDistanceTo(crossPoint);
if (distance < eps)
{
return true;
}
}
return false;
}
void mitk::ContourElement::Close()
{
this->m_IsClosed = true;
}
void mitk::ContourElement::Open()
{
this->m_IsClosed = false;
}
void mitk::ContourElement::SetClosed(bool isClosed)
{
isClosed ? this->Close() : this->Open();
}
mitk::ContourElement::VertexListType mitk::ContourElement::GetControlVertices() const
{
VertexListType controlVertices;
std::copy_if(
this->m_Vertices.begin(), this->m_Vertices.end(), std::back_inserter(controlVertices), [](const VertexType *v) {
return v->IsControlPoint;
});
return controlVertices;
}
void mitk::ContourElement::Concatenate(const mitk::ContourElement *other, bool check)
{
if (other->GetSize() > 0)
{
for (const auto &sourceVertex : other->m_Vertices)
{
if (check)
{
auto finding =
std::find_if(this->m_Vertices.begin(), this->m_Vertices.end(), [sourceVertex](const VertexType *v) {
return sourceVertex->Coordinates == v->Coordinates;
});
if (finding == this->m_Vertices.end())
{
this->m_Vertices.push_back(new ContourModelVertex(*sourceVertex));
}
}
else
{
this->m_Vertices.push_back(new ContourModelVertex(*sourceVertex));
}
}
}
}
mitk::ContourElement::VertexSizeType mitk::ContourElement::GetIndex(const VertexType *vertex) const
{
VertexSizeType result = NPOS;
auto finding = std::find(this->m_Vertices.begin(), this->m_Vertices.end(), vertex);
if (finding != this->m_Vertices.end())
{
result = finding - this->m_Vertices.begin();
}
return result;
}
bool mitk::ContourElement::RemoveVertex(const VertexType *vertex)
{
auto finding = std::find(this->m_Vertices.begin(), this->m_Vertices.end(), vertex);
return RemoveVertexByIterator(finding);
}
bool mitk::ContourElement::RemoveVertexAt(VertexSizeType index)
{
- if (index >= 0 && index < this->m_Vertices.size())
+ if (index < this->m_Vertices.size())
{
auto delIter = this->m_Vertices.begin() + index;
return RemoveVertexByIterator(delIter);
}
return false;
}
bool mitk::ContourElement::RemoveVertexAt(const mitk::Point3D &point, double eps)
{
if (eps > 0)
{
auto finding = std::find_if(this->m_Vertices.begin(), this->m_Vertices.end(), [point, eps](const VertexType *v) {
return v->Coordinates.EuclideanDistanceTo(point) < eps;
});
return RemoveVertexByIterator(finding);
}
return false;
}
bool mitk::ContourElement::RemoveVertexByIterator(VertexListType::iterator &iter)
{
if (iter != this->m_Vertices.end())
{
delete *iter;
this->m_Vertices.erase(iter);
return true;
}
return false;
}
void mitk::ContourElement::Clear()
{
for (auto vertex : m_Vertices)
{
delete vertex;
}
this->m_Vertices.clear();
}
//----------------------------------------------------------------------
void mitk::ContourElement::RedistributeControlVertices(const VertexType *selected, int period)
{
int counter = 0;
auto _where = this->m_Vertices.begin();
if (selected != nullptr)
{
auto finding = std::find(this->m_Vertices.begin(), this->m_Vertices.end(), selected);
if (finding != this->m_Vertices.end())
{
_where = finding;
}
}
auto _iter = _where;
while (_iter != this->m_Vertices.end())
{
div_t divresult;
divresult = div(counter, period);
(*_iter)->IsControlPoint = (divresult.rem == 0);
counter++;
_iter++;
}
_iter = _where;
counter = 0;
while (_iter != this->m_Vertices.begin())
{
div_t divresult;
divresult = div(counter, period);
(*_iter)->IsControlPoint = (divresult.rem == 0);
counter++;
_iter--;
}
}
diff --git a/Modules/ContourModel/DataManagement/mitkContourElement.h b/Modules/ContourModel/DataManagement/mitkContourElement.h
index 9a9f17186e..4a5e52864e 100644
--- a/Modules/ContourModel/DataManagement/mitkContourElement.h
+++ b/Modules/ContourModel/DataManagement/mitkContourElement.h
@@ -1,289 +1,290 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef _mitkContourElement_H_
#define _mitkContourElement_H_
#include "mitkCommon.h"
#include <MitkContourModelExports.h>
#include <mitkNumericTypes.h>
#include <deque>
namespace mitk
{
/** \brief Represents a contour in 3D space.
A ContourElement is consisting of linked vertices implicitely defining the contour.
They are stored in a double ended queue making it possible to add vertices at front and
end of the contour and to iterate in both directions.
To mark a vertex as a special one it can be set as a control point.
\note This class assumes that it manages its vertices. So if a vertex instance is added to this
class the ownership of the vertex is transfered to the ContourElement instance.
The ContourElement instance takes care of deleting vertex instances if needed.
It is highly not recommend to use this class directly as it is designed as a internal class of
ContourModel. Therefore it is adviced to use ContourModel if contour representations are needed in
MITK.
*/
class MITKCONTOURMODEL_EXPORT ContourElement : public itk::LightObject
{
public:
mitkClassMacroItkParent(ContourElement, itk::LightObject);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** \brief Represents a single vertex of a contour.
*/
struct MITKCONTOURMODEL_EXPORT ContourModelVertex
{
ContourModelVertex(const mitk::Point3D& point, bool active = false) : IsControlPoint(active), Coordinates(point) {};
ContourModelVertex(const ContourModelVertex& other)
: IsControlPoint(other.IsControlPoint), Coordinates(other.Coordinates)
{
};
/** \brief Treat point special. */
bool IsControlPoint;
/** \brief Coordinates in 3D space. */
mitk::Point3D Coordinates;
bool operator ==(const ContourModelVertex& other) const;
};
using VertexType = ContourModelVertex;
using VertexListType = std::deque<VertexType*>;
using VertexIterator = VertexListType::iterator;
using ConstVertexIterator = VertexListType::const_iterator;
using VertexSizeType = VertexListType::size_type;
/**Indicates an invalid index.
* It is always the maximum of the unsigned int type.*/
static const VertexSizeType NPOS = -1;
/** \brief Return a const iterator a the front.
*/
ConstVertexIterator ConstIteratorBegin() const;
/** \brief Return a const iterator a the end.
*/
ConstVertexIterator ConstIteratorEnd() const;
/** \brief Return an iterator a the front.
*/
VertexIterator IteratorBegin();
/** \brief Return an iterator a the end.
*/
VertexIterator IteratorEnd();
/** \brief Return a const iterator a the front.
* For easier support of stl functionality.
*/
ConstVertexIterator begin() const;
/** \brief Return a const iterator a the end.
* For easier support of stl functionality.
*/
ConstVertexIterator end() const;
/** \brief Return an iterator a the front.
* For easier support of stl functionality.
*/
VertexIterator begin();
/** \brief Return an iterator a the end.
* For easier support of stl functionality.
*/
VertexIterator end();
/** \brief Returns the number of contained vertices.
*/
VertexSizeType GetSize() const;
/** \brief Add a vertex at the end of the contour
\param point - coordinates in 3D space.
\param isControlPoint - is the vertex a special control point.
*/
void AddVertex(const mitk::Point3D &point, bool isControlPoint);
/** \brief Add a vertex at the front of the contour
\param point - coordinates in 3D space.
\param isControlPoint - is the vertex a control point.
*/
void AddVertexAtFront(const mitk::Point3D &point, bool isControlPoint);
/** \brief Add a vertex at a given index of the contour
\param point - coordinates in 3D space.
\param isControlPoint - is the vertex a special control point.
\param index - the index to be inserted at.
*/
void InsertVertexAtIndex(const mitk::Point3D &point, bool isControlPoint, VertexSizeType index);
/** \brief Set coordinates a given index.
\param pointId Index of vertex.
\param point Coordinates.
*/
void SetVertexAt(VertexSizeType pointId, const mitk::Point3D &point);
/** \brief Set vertex a given index (by copying the values).
\param pointId Index of vertex.
\param vertex Vertex.
\pre Passed vertex is a valid instance
*/
void SetVertexAt(VertexSizeType pointId, const VertexType* vertex);
/** \brief Returns the vertex a given index
\param index
\pre index must be valid.
*/
VertexType* GetVertexAt(VertexSizeType index);
const VertexType* GetVertexAt(VertexSizeType index) const;
/** \brief Returns the approximate nearest vertex a given position in 3D space
\param point - query position in 3D space.
\param eps - the error bound for search algorithm.
*/
VertexType *GetVertexAt(const mitk::Point3D &point, float eps);
/** \brief Returns the next vertex to the approximate nearest vertex of a given position in 3D space
\param point - query position in 3D space.
\param eps - the error bound for search algorithm.
*/
VertexType *GetNextControlVertexAt(const mitk::Point3D &point, float eps);
/** \brief Returns the previous vertex to the approximate nearest vertex of a given position in 3D space
\param point - query position in 3D space.
\param eps - the error bound for search algorithm.
*/
VertexType *GetPreviousControlVertexAt(const mitk::Point3D &point, float eps);
/** \brief Returns the approximate nearest control vertex a given posoition in 3D space, if the clicked position is within a specific range.
\param point - query position in 3D space.
\param eps - the error bound for search algorithm.
*/
VertexType *GetControlVertexAt(const mitk::Point3D &point, float eps);
/** \brief Returns the index of the given vertex within the contour.
\param vertex - the vertex to be searched.
\return index of vertex. Returns ContourElement::NPOS if not found.
*/
VertexSizeType GetIndex(const VertexType *vertex) const;
/** \brief Returns the container of the vertices.
*/
const VertexListType *GetVertexList() const;
/** \brief Returns whether the contour element is empty.
*/
bool IsEmpty() const;
/** \brief Returns if the conour is closed or not.
*/
bool IsClosed() const;
/** \brief Returns whether a given point is near a contour, according to eps.
\param point - query position in 3D space.
\param eps - the error bound for search algorithm.
*/
bool IsNearContour(const mitk::Point3D &point, float eps) const;
/** \brief Close the contour.
Connect first with last element.
*/
void Close();
/** \brief Open the contour.
Disconnect first and last element.
*/
void Open();
/** \brief Set the contours IsClosed property.
\param isClosed - true = closed; false = open;
*/
void SetClosed(bool isClosed);
/** \brief Concatenate the contuor with a another contour.
All vertices of the other contour will be cloned and added after last vertex.
\param other - the other contour
\param check - set it true to avoid adding of vertices that are already in the source contour
*/
void Concatenate(const mitk::ContourElement *other, bool check);
/** \brief Remove the given vertex from the container if exists.
\param vertex - the vertex to be removed.
*/
bool RemoveVertex(const VertexType *vertex);
/** \brief Remove a vertex at given index within the container if exists.
\param index - the index where the vertex should be removed.
*/
bool RemoveVertexAt(VertexSizeType index);
/** \brief Remove the approximate nearest vertex at given position in 3D space if one exists.
\param point - query point in 3D space.
\param eps - error bound for search algorithm.
*/
bool RemoveVertexAt(const mitk::Point3D &point, double eps);
/** \brief Clear the storage container.
*/
void Clear();
/** \brief Returns the approximate nearest vertex a given position in 3D space. With the parameter 'isControlPoint',
one can decide if any vertex should be returned, or just control vertices.
\param point - query position in 3D space.
\param eps - the error bound for search algorithm. It is an open boundary.
+ \param isControlPoint
\param offset - a offset to the vertex, e.g. 1 if the next vertex should be returned or -1 for the previous vertex
*/
VertexType *BruteForceGetVertexAt(const mitk::Point3D &point, double eps, bool isControlPoint = false, int offset = 0);
/** \brief Returns the index of the approximate nearest vertex of a given position in 3D space.
\param point - query position in 3D space.
\param eps - the error bound for search algorithm. It is an open boundary.
\param verticesList - the vertex list to search the index in, either only control vertices or all vertices
*/
int BruteForceGetVertexIndexAt(const mitk::Point3D &point,
double eps,
VertexListType verticesList);
/** Returns a list pointing to all vertices that are indicated to be control
points.
\remark It is important to note, that the vertex pointers in the returned
list directly point to the vertices stored interanlly. So they are still
owned by the ContourElement instance that returns the list. If one wants
to take over ownership, one has to clone the vertex instances.
*/
VertexListType GetControlVertices() const;
/** \brief Uniformly redistribute control points with a given period (in number of vertices)
\param vertex - the vertex around which the redistribution is done.
\param period - number of vertices between control points.
*/
void RedistributeControlVertices(const VertexType *vertex, int period);
protected:
mitkCloneMacro(Self);
ContourElement() = default;
ContourElement(const mitk::ContourElement &other);
~ContourElement();
ContourElement& operator = (const ContourElement & other);
/** Internal helper function to correctly remove the element indicated by the iterator
from the list. After the call the iterator is invalid.
Caller of the function must ensure that the iterator is valid!.
\result Indicates if the element indicated by the iterator was removed. If iterator points to end it returns false.*/
bool RemoveVertexByIterator(VertexListType::iterator& iter);
VertexListType m_Vertices; // double ended queue with vertices
bool m_IsClosed = false;
};
} // namespace mitk
#endif // _mitkContourElement_H_
diff --git a/Modules/ContourModel/DataManagement/mitkContourModel.cpp b/Modules/ContourModel/DataManagement/mitkContourModel.cpp
index 02350613d6..518abf4352 100644
--- a/Modules/ContourModel/DataManagement/mitkContourModel.cpp
+++ b/Modules/ContourModel/DataManagement/mitkContourModel.cpp
@@ -1,679 +1,690 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <mitkContourModel.h>
#include <mitkPlaneGeometry.h>
+namespace mitk
+{
+ itkEventMacroDefinition(ContourModelEvent, itk::AnyEvent);
+ itkEventMacroDefinition(ContourModelShiftEvent, ContourModelEvent);
+ itkEventMacroDefinition(ContourModelSizeChangeEvent, ContourModelEvent);
+ itkEventMacroDefinition(ContourModelAddEvent, ContourModelSizeChangeEvent);
+ itkEventMacroDefinition(ContourModelRemoveEvent, ContourModelSizeChangeEvent);
+ itkEventMacroDefinition(ContourModelExpandTimeBoundsEvent, ContourModelEvent);
+ itkEventMacroDefinition(ContourModelClosedEvent, ContourModelEvent);
+}
+
mitk::ContourModel::ContourModel() : m_UpdateBoundingBox(true)
{
// set to initial state
this->InitializeEmpty();
}
mitk::ContourModel::ContourModel(const ContourModel &other)
: BaseData(other), m_ContourSeries(other.m_ContourSeries), m_lineInterpolation(other.m_lineInterpolation)
{
m_SelectedVertex = nullptr;
}
mitk::ContourModel::~ContourModel()
{
m_SelectedVertex = nullptr;
this->m_ContourSeries.clear(); // TODO check destruction
}
void mitk::ContourModel::AddVertex(const Point3D &vertex, TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep))
{
this->AddVertex(vertex, false, timestep);
}
}
void mitk::ContourModel::AddVertex(const Point3D &vertex, bool isControlPoint, TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep))
{
this->m_ContourSeries[timestep]->AddVertex(vertex, isControlPoint);
this->InvokeEvent(ContourModelSizeChangeEvent());
this->Modified();
this->m_UpdateBoundingBox = true;
}
}
void mitk::ContourModel::AddVertex(const VertexType &vertex, TimeStepType timestep)
{
this->AddVertex(vertex.Coordinates, vertex.IsControlPoint, timestep);
}
void mitk::ContourModel::AddVertexAtFront(const Point3D &vertex, TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep))
{
this->AddVertexAtFront(vertex, false, timestep);
}
}
void mitk::ContourModel::AddVertexAtFront(const Point3D &vertex, bool isControlPoint, TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep))
{
this->m_ContourSeries[timestep]->AddVertexAtFront(vertex, isControlPoint);
this->InvokeEvent(ContourModelSizeChangeEvent());
this->Modified();
this->m_UpdateBoundingBox = true;
}
}
void mitk::ContourModel::AddVertexAtFront(const VertexType &vertex, TimeStepType timestep)
{
this->AddVertexAtFront(vertex.Coordinates, vertex.IsControlPoint, timestep);
}
bool mitk::ContourModel::SetVertexAt(int pointId, const Point3D &point, TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep))
{
if (pointId >= 0 && this->m_ContourSeries[timestep]->GetSize() > ContourElement::VertexSizeType(pointId))
{
this->m_ContourSeries[timestep]->SetVertexAt(pointId, point);
this->Modified();
this->m_UpdateBoundingBox = true;
return true;
}
return false;
}
return false;
}
bool mitk::ContourModel::SetVertexAt(int pointId, const VertexType *vertex, TimeStepType timestep)
{
if (vertex == nullptr)
return false;
if (!this->IsEmptyTimeStep(timestep))
{
if (pointId >= 0 && this->m_ContourSeries[timestep]->GetSize() > ContourElement::VertexSizeType(pointId))
{
this->m_ContourSeries[timestep]->SetVertexAt(pointId, vertex);
this->Modified();
this->m_UpdateBoundingBox = true;
return true;
}
return false;
}
return false;
}
void mitk::ContourModel::InsertVertexAtIndex(const Point3D &vertex, int index, bool isControlPoint, TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep))
{
if (index >= 0 && this->m_ContourSeries[timestep]->GetSize() > ContourElement::VertexSizeType(index))
{
this->m_ContourSeries[timestep]->InsertVertexAtIndex(vertex, isControlPoint, index);
this->InvokeEvent(ContourModelSizeChangeEvent());
this->Modified();
this->m_UpdateBoundingBox = true;
}
}
}
void mitk::ContourModel::UpdateContour(const ContourModel* sourceModel, TimeStepType destinationTimeStep, TimeStepType sourceTimeStep)
{
if (nullptr == sourceModel)
{
mitkThrow() << "Cannot update contour. Passed source model is invalid.";
}
if (!sourceModel->GetTimeGeometry()->IsValidTimeStep(sourceTimeStep))
{
mitkThrow() << "Cannot update contour. Source contour time geometry does not support passed time step. Invalid time step: " << sourceTimeStep;
}
if (!this->GetTimeGeometry()->IsValidTimeStep(destinationTimeStep))
{
MITK_WARN << "Cannot update contour. Contour time geometry does not support passed time step. Invalid time step: " << destinationTimeStep;
return;
}
this->Clear(destinationTimeStep);
std::for_each(sourceModel->Begin(sourceTimeStep), sourceModel->End(sourceTimeStep), [this, destinationTimeStep](ContourElement::VertexType* vertex) {
this->m_ContourSeries[destinationTimeStep]->AddVertex(vertex->Coordinates, vertex->IsControlPoint);
});
this->InvokeEvent(ContourModelSizeChangeEvent());
this->Modified();
this->m_UpdateBoundingBox = true;
}
bool mitk::ContourModel::IsEmpty(TimeStepType timestep) const
{
if (!this->IsEmptyTimeStep(timestep))
{
return this->m_ContourSeries[timestep]->IsEmpty();
}
return true;
}
bool mitk::ContourModel::IsEmpty() const
{
return this->IsEmpty(0);
}
int mitk::ContourModel::GetNumberOfVertices(TimeStepType timestep) const
{
if (!this->IsEmptyTimeStep(timestep))
{
return this->m_ContourSeries[timestep]->GetSize();
}
return -1;
}
const mitk::ContourModel::VertexType *mitk::ContourModel::GetVertexAt(int index, TimeStepType timestep) const
{
if (!this->IsEmptyTimeStep(timestep) && this->m_ContourSeries[timestep]->GetSize() > mitk::ContourElement::VertexSizeType(index))
{
return this->m_ContourSeries[timestep]->GetVertexAt(index);
}
return nullptr;
}
const mitk::ContourModel::VertexType *mitk::ContourModel::GetNextControlVertexAt(mitk::Point3D &point,
float eps,
TimeStepType timestep) const
{
if (!this->IsEmptyTimeStep(timestep))
{
return this->m_ContourSeries[timestep]->GetNextControlVertexAt(point, eps);
}
return nullptr;
}
const mitk::ContourModel::VertexType *mitk::ContourModel::GetPreviousControlVertexAt(mitk::Point3D &point,
float eps,
TimeStepType timestep) const
{
if (!this->IsEmptyTimeStep(timestep))
{
return this->m_ContourSeries[timestep]->GetPreviousControlVertexAt(point, eps);
}
return nullptr;
}
int mitk::ContourModel::GetIndex(const VertexType *vertex, TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep))
{
return this->m_ContourSeries[timestep]->GetIndex(vertex);
}
return -1;
}
void mitk::ContourModel::Close(TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep))
{
this->m_ContourSeries[timestep]->Close();
this->InvokeEvent(ContourModelClosedEvent());
this->Modified();
this->m_UpdateBoundingBox = true;
}
}
void mitk::ContourModel::Open(TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep))
{
this->m_ContourSeries[timestep]->Open();
this->InvokeEvent(ContourModelClosedEvent());
this->Modified();
this->m_UpdateBoundingBox = true;
}
}
void mitk::ContourModel::SetClosed(bool isClosed, TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep))
{
this->m_ContourSeries[timestep]->SetClosed(isClosed);
this->InvokeEvent(ContourModelClosedEvent());
this->Modified();
this->m_UpdateBoundingBox = true;
}
}
bool mitk::ContourModel::IsEmptyTimeStep(unsigned int t) const
{
return (this->m_ContourSeries.size() <= t);
}
bool mitk::ContourModel::IsNearContour(Point3D &point, float eps, TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep))
{
return this->m_ContourSeries[timestep]->IsNearContour(point, eps);
}
return false;
}
void mitk::ContourModel::Concatenate(ContourModel *other, TimeStepType timestep, bool check)
{
if (!this->IsEmptyTimeStep(timestep))
{
if (!this->m_ContourSeries[timestep]->IsClosed())
{
this->m_ContourSeries[timestep]->Concatenate(other->m_ContourSeries[timestep], check);
this->InvokeEvent(ContourModelSizeChangeEvent());
this->Modified();
this->m_UpdateBoundingBox = true;
}
}
}
mitk::ContourModel::VertexIterator mitk::ContourModel::Begin(TimeStepType timestep) const
{
return this->IteratorBegin(timestep);
}
mitk::ContourModel::VertexIterator mitk::ContourModel::IteratorBegin(TimeStepType timestep) const
{
if (!this->IsEmptyTimeStep(timestep))
{
return this->m_ContourSeries[timestep]->IteratorBegin();
}
else
{
mitkThrow() << "No iterator at invalid timestep " << timestep << ". There are only " << this->GetTimeSteps()
<< " timesteps available.";
}
}
mitk::ContourModel::VertexIterator mitk::ContourModel::End(TimeStepType timestep) const
{
return this->IteratorEnd(timestep);
}
mitk::ContourModel::VertexIterator mitk::ContourModel::IteratorEnd(TimeStepType timestep) const
{
if (!this->IsEmptyTimeStep(timestep))
{
return this->m_ContourSeries[timestep]->IteratorEnd();
}
else
{
mitkThrow() << "No iterator at invalid timestep " << timestep << ". There are only " << this->GetTimeSteps()
<< " timesteps available.";
}
}
bool mitk::ContourModel::IsClosed(int timestep) const
{
if (!this->IsEmptyTimeStep(timestep))
{
return this->m_ContourSeries[timestep]->IsClosed();
}
return false;
}
bool mitk::ContourModel::SelectControlVertexAt(Point3D &point, float eps, TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep))
{
this->m_SelectedVertex = this->m_ContourSeries[timestep]->GetControlVertexAt(point, eps);
}
return this->m_SelectedVertex != nullptr;
}
bool mitk::ContourModel::SelectVertexAt(Point3D &point, float eps, TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep))
{
this->m_SelectedVertex = this->m_ContourSeries[timestep]->GetVertexAt(point, eps);
}
return this->m_SelectedVertex != nullptr;
}
bool mitk::ContourModel::SelectVertexAt(int index, TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep) && index >= 0)
{
return (this->m_SelectedVertex = this->m_ContourSeries[timestep]->GetVertexAt(index));
}
return false;
}
bool mitk::ContourModel::SetControlVertexAt(Point3D &point, float eps, TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep))
{
VertexType *vertex = this->m_ContourSeries[timestep]->GetVertexAt(point, eps);
if (vertex != nullptr)
{
vertex->IsControlPoint = true;
return true;
}
}
return false;
}
bool mitk::ContourModel::SetControlVertexAt(int index, TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep) && index >= 0)
{
VertexType *vertex = this->m_ContourSeries[timestep]->GetVertexAt(index);
if (vertex != nullptr)
{
vertex->IsControlPoint = true;
return true;
}
}
return false;
}
bool mitk::ContourModel::RemoveVertex(const VertexType *vertex, TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep))
{
if (this->m_ContourSeries[timestep]->RemoveVertex(vertex))
{
this->Modified();
this->m_UpdateBoundingBox = true;
this->InvokeEvent(ContourModelSizeChangeEvent());
return true;
}
}
return false;
}
bool mitk::ContourModel::RemoveVertexAt(int index, TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep))
{
if (this->m_ContourSeries[timestep]->RemoveVertexAt(index))
{
this->Modified();
this->m_UpdateBoundingBox = true;
this->InvokeEvent(ContourModelSizeChangeEvent());
return true;
}
}
return false;
}
bool mitk::ContourModel::RemoveVertexAt(Point3D &point, float eps, TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep))
{
if (this->m_ContourSeries[timestep]->RemoveVertexAt(point, eps))
{
this->Modified();
this->m_UpdateBoundingBox = true;
this->InvokeEvent(ContourModelSizeChangeEvent());
return true;
}
}
return false;
}
void mitk::ContourModel::ShiftSelectedVertex(Vector3D &translate)
{
if (this->m_SelectedVertex)
{
this->ShiftVertex(this->m_SelectedVertex, translate);
this->Modified();
this->m_UpdateBoundingBox = true;
}
}
void mitk::ContourModel::ShiftContour(Vector3D &translate, TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep))
{
// shift all vertices
for (auto vertex : *(this->m_ContourSeries[timestep]))
{
this->ShiftVertex(vertex, translate);
}
this->Modified();
this->m_UpdateBoundingBox = true;
this->InvokeEvent(ContourModelShiftEvent());
}
}
void mitk::ContourModel::ShiftVertex(VertexType *vertex, Vector3D &vector)
{
vertex->Coordinates[0] += vector[0];
vertex->Coordinates[1] += vector[1];
vertex->Coordinates[2] += vector[2];
}
void mitk::ContourModel::Clear(TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep))
{
// clear data at timestep
this->m_ContourSeries[timestep]->Clear();
this->Modified();
this->m_UpdateBoundingBox = true;
}
}
void mitk::ContourModel::Expand(unsigned int timeSteps)
{
std::size_t oldSize = this->m_ContourSeries.size();
if (static_cast<std::size_t>(timeSteps) > oldSize)
{
Superclass::Expand(timeSteps);
// insert contours for each new timestep
for (std::size_t i = oldSize; i < static_cast<std::size_t>(timeSteps); i++)
{
m_ContourSeries.push_back(ContourElement::New());
}
this->InvokeEvent(ContourModelExpandTimeBoundsEvent());
}
}
void mitk::ContourModel::SetRequestedRegionToLargestPossibleRegion()
{
// no support for regions
}
bool mitk::ContourModel::RequestedRegionIsOutsideOfTheBufferedRegion()
{
// no support for regions
return false;
}
bool mitk::ContourModel::VerifyRequestedRegion()
{
// no support for regions
return true;
}
void mitk::ContourModel::SetRequestedRegion(const itk::DataObject * /*data*/)
{
// no support for regions
}
void mitk::ContourModel::Clear()
{
// clear data and set to initial state again
this->ClearData();
this->InitializeEmpty();
this->Modified();
this->m_UpdateBoundingBox = true;
}
void mitk::ContourModel::RedistributeControlVertices(int period, TimeStepType timestep)
{
if (!this->IsEmptyTimeStep(timestep))
{
this->m_ContourSeries[timestep]->RedistributeControlVertices(this->GetSelectedVertex(), period);
this->InvokeEvent(ContourModelClosedEvent());
this->Modified();
this->m_UpdateBoundingBox = true;
}
}
mitk::ContourModel::VertexListType mitk::ContourModel::GetControlVertices(TimeStepType timestep)
{
VertexListType controlVertices;
if (!this->IsEmptyTimeStep(timestep))
{
controlVertices = this->m_ContourSeries[timestep]->GetControlVertices();
}
return controlVertices;
}
mitk::ContourModel::VertexListType mitk::ContourModel::GetVertexList(TimeStepType timestep)
{
VertexListType controlVertices;
if (!this->IsEmptyTimeStep(timestep))
{
controlVertices = *this->m_ContourSeries[timestep]->GetVertexList();
}
return controlVertices;
}
void mitk::ContourModel::ClearData()
{
// call the superclass, this releases the data of BaseData
Superclass::ClearData();
// clear out the time resolved contours
this->m_ContourSeries.clear();
}
void mitk::ContourModel::Initialize()
{
this->InitializeEmpty();
this->Modified();
this->m_UpdateBoundingBox = true;
}
void mitk::ContourModel::Initialize(const ContourModel &other)
{
TimeStepType numberOfTimesteps = other.GetTimeGeometry()->CountTimeSteps();
this->InitializeTimeGeometry(numberOfTimesteps);
for (TimeStepType currentTimestep = 0; currentTimestep < numberOfTimesteps; currentTimestep++)
{
this->m_ContourSeries.push_back(ContourElement::New());
this->SetClosed(other.IsClosed(currentTimestep), currentTimestep);
}
m_SelectedVertex = nullptr;
this->m_lineInterpolation = other.m_lineInterpolation;
this->Modified();
this->m_UpdateBoundingBox = true;
}
void mitk::ContourModel::InitializeEmpty()
{
// clear data at timesteps
this->m_ContourSeries.resize(0);
this->m_ContourSeries.push_back(ContourElement::New());
// set number of timesteps to one
this->InitializeTimeGeometry(1);
m_SelectedVertex = nullptr;
this->m_lineInterpolation = ContourModel::LINEAR;
}
void mitk::ContourModel::UpdateOutputInformation()
{
if (this->GetSource())
{
this->GetSource()->UpdateOutputInformation();
}
if (this->m_UpdateBoundingBox)
{
// update the bounds of the geometry according to the stored vertices
ScalarType mitkBounds[6];
// calculate the boundingbox at each timestep
typedef itk::BoundingBox<unsigned long, 3, ScalarType> BoundingBoxType;
typedef BoundingBoxType::PointsContainer PointsContainer;
int timesteps = this->GetTimeSteps();
// iterate over the timesteps
for (int currenTimeStep = 0; currenTimeStep < timesteps; currenTimeStep++)
{
if (dynamic_cast<PlaneGeometry *>(this->GetGeometry(currenTimeStep)))
{
// do not update bounds for 2D geometries, as they are unfortunately defined with min bounds 0!
return;
}
else
{ // we have a 3D geometry -> let's update bounds
// only update bounds if the contour was modified
if (this->GetMTime() > this->GetGeometry(currenTimeStep)->GetBoundingBox()->GetMTime())
{
mitkBounds[0] = 0.0;
mitkBounds[1] = 0.0;
mitkBounds[2] = 0.0;
mitkBounds[3] = 0.0;
mitkBounds[4] = 0.0;
mitkBounds[5] = 0.0;
BoundingBoxType::Pointer boundingBox = BoundingBoxType::New();
PointsContainer::Pointer points = PointsContainer::New();
auto it = this->IteratorBegin(currenTimeStep);
auto end = this->IteratorEnd(currenTimeStep);
// fill the boundingbox with the points
while (it != end)
{
Point3D currentP = (*it)->Coordinates;
BoundingBoxType::PointType p;
p.CastFrom(currentP);
points->InsertElement(points->Size(), p);
it++;
}
// construct the new boundingBox
boundingBox->SetPoints(points);
boundingBox->ComputeBoundingBox();
BoundingBoxType::BoundsArrayType tmp = boundingBox->GetBounds();
mitkBounds[0] = tmp[0];
mitkBounds[1] = tmp[1];
mitkBounds[2] = tmp[2];
mitkBounds[3] = tmp[3];
mitkBounds[4] = tmp[4];
mitkBounds[5] = tmp[5];
// set boundingBox at current timestep
BaseGeometry *geometry3d = this->GetGeometry(currenTimeStep);
geometry3d->SetBounds(mitkBounds);
}
}
}
this->m_UpdateBoundingBox = false;
}
GetTimeGeometry()->Update();
}
void mitk::ContourModel::ExecuteOperation(Operation * /*operation*/)
{
// not supported yet
}
diff --git a/Modules/ContourModel/DataManagement/mitkContourModel.h b/Modules/ContourModel/DataManagement/mitkContourModel.h
index f42452d056..f88e13b358 100644
--- a/Modules/ContourModel/DataManagement/mitkContourModel.h
+++ b/Modules/ContourModel/DataManagement/mitkContourModel.h
@@ -1,467 +1,467 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef _MITK_CONTOURMODEL_H_
#define _MITK_CONTOURMODEL_H_
#include "mitkBaseData.h"
#include "mitkCommon.h"
#include <MitkContourModelExports.h>
#include <mitkContourElement.h>
namespace mitk
{
/**
\brief ContourModel is a structure of linked vertices defining a contour in 3D space.
The vertices are stored in a mitk::ContourElement is stored for each timestep.
The contour line segments are implicitly defined by the given linked vertices.
By default two control points are are linked by a straight line.It is possible to add
vertices at front and end of the contour and to iterate in both directions.
Points are specified containing coordinates and additional (data) information,
see mitk::ContourElement.
For accessing a specific vertex either an index or a position in 3D Space can be used.
The vertices are best accessed by using a VertexIterator.
Interaction with the contour is thus available without any mitk interactor class using the
api of ContourModel. It is possible to shift single vertices also as shifting the whole
contour.
A contour can be either open like a single curved line segment or
closed. A closed contour can for example represent a jordan curve.
\section mitkContourModelDisplayOptions Display Options
The default mappers for this data structure are mitk::ContourModelGLMapper2D and
mitk::ContourModelMapper3D. See these classes for display options which can
can be set via properties.
*/
class MITKCONTOURMODEL_EXPORT ContourModel : public BaseData
{
public:
mitkClassMacro(ContourModel, BaseData);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/*+++++++++++++++ typedefs +++++++++++++++++++++++++++++++*/
typedef ContourElement::VertexType VertexType;
typedef ContourElement::VertexListType VertexListType;
typedef ContourElement::VertexIterator VertexIterator;
typedef ContourElement::ConstVertexIterator ConstVertexIterator;
typedef std::vector<ContourElement::Pointer> ContourModelSeries;
/*+++++++++++++++ END typedefs ++++++++++++++++++++++++++++*/
/** \brief Possible interpolation of the line segments between control points */
enum LineSegmentInterpolation
{
LINEAR,
B_SPLINE
};
/*++++++++++++++++ inline methods +++++++++++++++++++++++*/
/** \brief Get the current selected vertex.
*/
VertexType *GetSelectedVertex() { return this->m_SelectedVertex; }
/** \brief Deselect vertex.
*/
void Deselect() { this->m_SelectedVertex = nullptr; }
/** \brief Set selected vertex as control point
*/
void SetSelectedVertexAsControlPoint(bool isControlPoint = true)
{
if (this->m_SelectedVertex)
{
m_SelectedVertex->IsControlPoint = isControlPoint;
this->Modified();
}
}
/** \brief Set the interpolation of the line segments between control points.
*/
void SetLineSegmentInterpolation(LineSegmentInterpolation interpolation)
{
this->m_lineInterpolation = interpolation;
this->Modified();
}
/** \brief Get the interpolation of the line segments between control points.
*/
LineSegmentInterpolation GetLineSegmentInterpolation() { return this->m_lineInterpolation; }
/*++++++++++++++++ END inline methods +++++++++++++++++++++++*/
/** \brief Add a vertex to the contour at given timestep.
The vertex is added at the end of contour.
\param vertex - coordinate representation of a control point
\param timestep - the timestep at which the vertex will be add ( default 0)
@note Adding a vertex to a timestep which exceeds the timebounds of the contour
will not be added, the TimeGeometry will not be expanded.
*/
void AddVertex(const Point3D &vertex, TimeStepType timestep = 0);
/** \brief Add a vertex to the contour at given timestep.
A copy of the passed vertex is added at the end of contour.
\param vertex - coordinate representation of a control point
\param timestep - the timestep at which the vertex will be add ( default 0)
@note Adding a vertex to a timestep which exceeds the timebounds of the contour
will not be added, the TimeGeometry will not be expanded.
*/
void AddVertex(const VertexType &vertex, TimeStepType timestep = 0);
/** \brief Add a vertex to the contour.
\param vertex - coordinate representation of a control point
\param timestep - the timestep at which the vertex will be add ( default 0)
\param isControlPoint - specifies the vertex to be handled in a special way (e.g. control points
will be rendered).
@note Adding a vertex to a timestep which exceeds the timebounds of the contour
will not be added, the TimeGeometry will not be expanded.
*/
void AddVertex(const Point3D& vertex, bool isControlPoint, TimeStepType timestep = 0);
/** Clears the contour of destinationTimeStep and copies
the contour of the passed source model at the sourceTimeStep.
@pre soureModel must point to a valid instance
@pre sourceTimePoint must be valid
@note Updateing a vertex to a timestep which exceeds the timebounds of the contour
will not be added, the TimeGeometry will not be expanded.
*/
void UpdateContour(const ContourModel* sourceModel, TimeStepType destinationTimeStep, TimeStepType sourceTimeStep);
/** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour.
The vertex is added at the FRONT of contour.
\param vertex - coordinate representation of a control point
\param timestep - the timestep at which the vertex will be add ( default 0)
@note Adding a vertex to a timestep which exceeds the timebounds of the contour
will not be added, the TimeGeometry will not be expanded.
*/
void AddVertexAtFront(const Point3D &vertex, TimeStepType timestep = 0);
/** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour.
The vertex is added at the FRONT of contour.
\param vertex - coordinate representation of a control point
\param timestep - the timestep at which the vertex will be add ( default 0)
@note Adding a vertex to a timestep which exceeds the timebounds of the contour
will not be added, the TimeGeometry will not be expanded.
*/
void AddVertexAtFront(const VertexType &vertex, TimeStepType timestep = 0);
/** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour.
\param vertex - coordinate representation of a control point
\param timestep - the timestep at which the vertex will be add ( default 0)
\param isControlPoint - specifies the vertex to be handled in a special way (e.g. control points
will be rendered).
@note Adding a vertex to a timestep which exceeds the timebounds of the contour
will not be added, the TimeGeometry will not be expanded.
*/
void AddVertexAtFront(const Point3D &vertex, bool isControlPoint, TimeStepType timestep = 0);
/** \brief Insert a vertex at given index.
*/
void InsertVertexAtIndex(const Point3D &vertex, int index, bool isControlPoint = false, TimeStepType timestep = 0);
/** \brief Set a coordinates for point at given index.
*/
bool SetVertexAt(int pointId, const Point3D &point, TimeStepType timestep = 0);
/** \brief Set a coordinates and control state for point at given index.
*/
bool SetVertexAt(int pointId, const VertexType *vertex, TimeStepType timestep = 0);
/** \brief Return if the contour is closed or not.
*/
bool IsClosed(int timestep = 0) const;
/** \brief Concatenate two contours.
The starting control point of the other will be added at the end of the contour.
\param other
\param timestep - the timestep at which the vertex will be add ( default 0)
\param check - check for intersections ( default false)
*/
void Concatenate(ContourModel *other, TimeStepType timestep = 0, bool check = false);
/** \brief Returns a const VertexIterator at the start element of the contour.
@throw mitk::Exception if the timestep is invalid.
*/
VertexIterator Begin(TimeStepType timestep = 0) const;
/** \brief Returns a const VertexIterator at the start element of the contour.
@throw mitk::Exception if the timestep is invalid.
*/
VertexIterator IteratorBegin(TimeStepType timestep = 0) const;
/** \brief Returns a const VertexIterator at the end element of the contour.
@throw mitk::Exception if the timestep is invalid.
*/
VertexIterator End(TimeStepType timestep = 0) const;
/** \brief Returns a const VertexIterator at the end element of the contour.
@throw mitk::Exception if the timestep is invalid.
*/
VertexIterator IteratorEnd(TimeStepType timestep = 0) const;
/** \brief Close the contour.
The last control point will be linked with the first point.
*/
virtual void Close(TimeStepType timestep = 0);
/** \brief Set isClosed to false contour.
The link between the last control point the first point will be removed.
*/
virtual void Open(TimeStepType timestep = 0);
/** \brief Set closed property to given boolean.
false - The link between the last control point the first point will be removed.
true - The last control point will be linked with the first point.
*/
virtual void SetClosed(bool isClosed, TimeStepType timestep = 0);
/** \brief Returns the number of vertices at a given timestep.
\param timestep - default = 0
*/
int GetNumberOfVertices(TimeStepType timestep = 0) const;
/** \brief Returns whether the contour model is empty at a given timestep.
\param timestep - default = 0
*/
virtual bool IsEmpty(TimeStepType timestep) const;
/** \brief Returns whether the contour model is empty.
*/
bool IsEmpty() const override;
/** \brief Returns the vertex at the index position within the container.
* If the index or timestep is invalid a nullptr will be returned.
*/
virtual const VertexType *GetVertexAt(int index, TimeStepType timestep = 0) const;
/** Returns the next control vertex to the approximate nearest vertex of a given position in 3D space
* If the timestep is invalid a nullptr will be returned.
*/
virtual const VertexType *GetNextControlVertexAt(mitk::Point3D &point, float eps, TimeStepType timestep) const;
/** Returns the previous control vertex to the approximate nearest vertex of a given position in 3D space
* If the timestep is invalid a nullptr will be returned.
*/
virtual const VertexType *GetPreviousControlVertexAt(mitk::Point3D &point, float eps, TimeStepType timestep) const;
/** \brief Remove a vertex at given timestep within the container.
\return index of vertex. -1 if not found.
*/
int GetIndex(const VertexType *vertex, TimeStepType timestep = 0);
/** \brief Check if there isn't something at this timestep.
*/
bool IsEmptyTimeStep(unsigned int t) const override;
/** \brief Check if mouse cursor is near the contour.
*/
virtual bool IsNearContour(Point3D &point, float eps, TimeStepType timestep);
/** \brief Mark a vertex at an index in the container as selected.
*/
bool SelectVertexAt(int index, TimeStepType timestep = 0);
/** \brief Mark a vertex at an index in the container as control point.
*/
bool SetControlVertexAt(int index, TimeStepType timestep = 0);
/** \brief Mark a control vertex at a given position in 3D space.
\param point - query point in 3D space
\param eps - radius for nearest neighbour search (error bound).
\param timestep - search at this timestep
@return true = vertex found; false = no vertex found
*/
bool SelectControlVertexAt(Point3D &point, float eps, TimeStepType timestep = 0);
/** \brief Mark a vertex at a given position in 3D space.
\param point - query point in 3D space
\param eps - radius for nearest neighbour search (error bound).
\param timestep - search at this timestep
@return true = vertex found; false = no vertex found
*/
bool SelectVertexAt(Point3D &point, float eps, TimeStepType timestep = 0);
/*
\pararm point - query point in 3D space
\pararm eps - radius for nearest neighbour search (error bound).
\pararm timestep - search at this timestep
@return true = vertex found; false = no vertex found
*/
bool SetControlVertexAt(Point3D &point, float eps, TimeStepType timestep = 0);
/** \brief Remove a vertex at given index within the container.
@return true = the vertex was successfuly removed; false = wrong index.
*/
bool RemoveVertexAt(int index, TimeStepType timestep = 0);
/** \brief Remove a vertex at given timestep within the container.
@return true = the vertex was successfuly removed.
*/
bool RemoveVertex(const VertexType *vertex, TimeStepType timestep = 0);
/** \brief Remove a vertex at a query position in 3D space.
The vertex to be removed will be search by nearest neighbour search.
Note that possibly no vertex at this position and eps is stored inside
the contour.
@return true = the vertex was successfuly removed; false = no vertex found.
*/
bool RemoveVertexAt(Point3D &point, float eps, TimeStepType timestep = 0);
/** \brief Shift the currently selected vertex by a translation vector.
\param translate - the translation vector.
*/
void ShiftSelectedVertex(Vector3D &translate);
/** \brief Shift the whole contour by a translation vector at given timestep.
\param translate - the translation vector.
\param timestep - at this timestep the contour will be shifted.
*/
void ShiftContour(Vector3D &translate, TimeStepType timestep = 0);
/** \brief Clear the storage container at given timestep.
All control points are removed at
timestep.
*/
virtual void Clear(TimeStepType timestep);
/** \brief Initialize all data objects
*/
void Initialize() override;
/** \brief Initialize object with specs of other contour.
Note: No data will be copied.
*/
void Initialize(const ContourModel &other);
/** \brief Returns a list pointing to all vertices that are indicated to be control points.
*/
VertexListType GetControlVertices(TimeStepType timestep);
/** \brief Returns the container of the vertices.
*/
VertexListType GetVertexList(TimeStepType timestep);
/*++++++++++++++++++ method inherit from base data +++++++++++++++++++++++++++*/
/**
\brief Inherit from base data - no region support available for contourModel objects.
*/
void SetRequestedRegionToLargestPossibleRegion() override;
/**
\brief Inherit from base data - no region support available for contourModel objects.
*/
bool RequestedRegionIsOutsideOfTheBufferedRegion() override;
/**
\brief Inherit from base data - no region support available for contourModel objects.
*/
bool VerifyRequestedRegion() override;
/**
\brief Inherit from base data - no region support available for contourModel objects.
*/
void SetRequestedRegion(const itk::DataObject *data) override;
/**
\brief Expand the contour model and its TimeGeometry to given number of timesteps.
*/
void Expand(unsigned int timeSteps) override;
/**
\brief Update the OutputInformation of a ContourModel object
The BoundingBox of the contour will be updated, if necessary.
*/
void UpdateOutputInformation() override;
/**
\brief Clear the storage container.
The object is set to initial state. All control points are removed and the number of
timesteps are set to 1.
*/
void Clear() override;
/**
\brief overwrite if the Data can be called by an Interactor (StateMachine).
*/
void ExecuteOperation(Operation *operation) override;
/** \brief Redistributes ontrol vertices with a given period (as number of vertices)
\param period - the number of vertices between control points.
\param timestep - at this timestep all lines will be rebuilt.
*/
virtual void RedistributeControlVertices(int period, TimeStepType timestep);
protected:
mitkCloneMacro(Self);
ContourModel();
ContourModel(const ContourModel &other);
~ContourModel() override;
// inherit from BaseData. called by Clear()
void ClearData() override;
// inherit from BaseData. Initial state of a contour with no vertices and a single timestep.
void InitializeEmpty() override;
// Shift a vertex
static void ShiftVertex(VertexType *vertex, Vector3D &vector);
// Storage with time resolved support.
ContourModelSeries m_ContourSeries;
// The currently selected vertex.
VertexType *m_SelectedVertex;
// The interpolation of the line segment between control points.
LineSegmentInterpolation m_lineInterpolation;
// only update the bounding geometry if necessary
bool m_UpdateBoundingBox;
};
- itkEventMacro(ContourModelEvent, itk::AnyEvent);
- itkEventMacro(ContourModelShiftEvent, ContourModelEvent);
- itkEventMacro(ContourModelSizeChangeEvent, ContourModelEvent);
- itkEventMacro(ContourModelAddEvent, ContourModelSizeChangeEvent);
- itkEventMacro(ContourModelRemoveEvent, ContourModelSizeChangeEvent);
- itkEventMacro(ContourModelExpandTimeBoundsEvent, ContourModelEvent);
- itkEventMacro(ContourModelClosedEvent, ContourModelEvent);
+ itkEventMacroDeclaration(ContourModelEvent, itk::AnyEvent);
+ itkEventMacroDeclaration(ContourModelShiftEvent, ContourModelEvent);
+ itkEventMacroDeclaration(ContourModelSizeChangeEvent, ContourModelEvent);
+ itkEventMacroDeclaration(ContourModelAddEvent, ContourModelSizeChangeEvent);
+ itkEventMacroDeclaration(ContourModelRemoveEvent, ContourModelSizeChangeEvent);
+ itkEventMacroDeclaration(ContourModelExpandTimeBoundsEvent, ContourModelEvent);
+ itkEventMacroDeclaration(ContourModelClosedEvent, ContourModelEvent);
}
#endif
diff --git a/Modules/ContourModel/IO/mitkContourModelWriter.h b/Modules/ContourModel/IO/mitkContourModelWriter.h
index 0add146a3b..639fec3e2d 100644
--- a/Modules/ContourModel/IO/mitkContourModelWriter.h
+++ b/Modules/ContourModel/IO/mitkContourModelWriter.h
@@ -1,158 +1,160 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef _MITK_CONTOURMODEL_WRITER__H_
#define _MITK_CONTOURMODEL_WRITER__H_
#include <mitkAbstractFileWriter.h>
#include <mitkContourModel.h>
// DEPRECATED
#include <mitkTimeGeometry.h>
namespace mitk
{
/**
* @brief XML-based writer for mitk::ContourModels
*
* XML-based writer for mitk::ContourModels. Multiple ContourModels can be written in
* a single XML file by simply setting multiple inputs to the filter.
*
* The xml file will look like:
*
+ * \code{.unparsed}
* <?xml version="1.0" encoding="utf-8"?>
* <contourModel>
* <head>
* <geometryInfo>
* </geometryInfo>
* </head>
* <data>
* <timestep n="0">
* <controlPoints>
* <point>
* <x></x>
* <y></y>
* <z></z>
* </point>
* </controlPoint>
* </timestep>
* </data>
* </contourModel>
+ * \endcode
*
* @ingroup MitkContourModelModule
*/
class ContourModelWriter : public mitk::AbstractFileWriter
{
public:
explicit ContourModelWriter(bool writeXMLHeader = true);
~ContourModelWriter() override;
using AbstractFileWriter::Write;
void Write() override;
protected:
ContourModelWriter(const ContourModelWriter &other);
mitk::ContourModelWriter *Clone() const override;
/**
* Converts an arbitrary type to a string. The type has to
* support the << operator. This works fine at least for integral
* data types as float, int, long etc.
* @param value the value to convert
* @returns the string representation of value
*/
template <typename T>
std::string ConvertToString(T value);
/**
* Writes an XML representation of the given point set to
* an outstream. The XML-Header an root node is not included!
* @param contourModel the point set to be converted to xml
* @param out the stream to write to.
*/
void WriteXML(const mitk::ContourModel *contourModel, std::ostream &out);
/**
* Writes the geometry information of the TimeGeometry to an outstream.
* The root tag is not included.
* @param geometry the TimeGeometry of the contour.
* @param out the stream to write to.
*/
void WriteGeometryInformation(const mitk::TimeGeometry *geometry, std::ostream &out);
/**
* Writes an standard xml header to the given stream.
* @param file the stream in which the header is written.
*/
void WriteXMLHeader(std::ostream &file);
/** Write a start element tag */
void WriteStartElement(const char *const tag, std::ostream &file);
void WriteStartElementWithAttribut(const char *const tag,
std::vector<std::string> attributes,
std::vector<std::string> values,
std::ostream &file);
/**
* Write an end element tag
* End-Elements following character data should pass indent = false.
*/
void WriteEndElement(const char *const tag, std::ostream &file, const bool &indent = true);
/** Write character data inside a tag. */
void WriteCharacterData(const char *const data, std::ostream &file);
/** Write a start element tag */
void WriteStartElement(std::string &tag, std::ostream &file);
/** Write an end element tag */
void WriteEndElement(std::string &tag, std::ostream &file, const bool &indent = true);
/** Write character data inside a tag. */
void WriteCharacterData(std::string &data, std::ostream &file);
/** Writes empty spaces to the stream according to m_IndentDepth and m_Indent */
void WriteIndent(std::ostream &file);
bool m_WriteXMLHeader;
unsigned int m_IndentDepth;
unsigned int m_Indent;
public:
static const char *XML_CONTOURMODEL;
static const char *XML_HEAD;
static const char *XML_GEOMETRY_INFO;
static const char *XML_DATA;
static const char *XML_TIME_STEP;
static const char *XML_CONTROL_POINTS;
static const char *XML_POINT;
static const char *XML_X;
static const char *XML_Y;
static const char *XML_Z;
};
}
#endif
diff --git a/Modules/Core/CMakeLists.txt b/Modules/Core/CMakeLists.txt
index b38af61df6..27d967637d 100644
--- a/Modules/Core/CMakeLists.txt
+++ b/Modules/Core/CMakeLists.txt
@@ -1,77 +1,77 @@
set(TOOL_CPPS "")
# temporary suppress warnings in the following files until image accessors are fully integrated.
set_source_files_properties( src/DataManagement/mitkImage.cpp COMPILE_FLAGS -DMITK_NO_DEPRECATED_WARNINGS )
set_source_files_properties( src/Controllers/mitkSliceNavigationController.cpp COMPILE_FLAGS -DMITK_NO_DEPRECATED_WARNINGS )
#if(MSVC)
# set(optional_private_package_depends psapi)
#endif()
mitk_create_module(
INCLUDE_DIRS
PUBLIC
${MITK_BINARY_DIR}
PRIVATE
src/Algorithms
src/Controllers
src/DataManagement
src/Interactions
src/IO
src/Rendering
DEPENDS
PUBLIC
mbilog
CppMicroServices
PACKAGE_DEPENDS
PUBLIC
Boost
ITK|IOImageBase+SpatialObjects+Statistics
#ITK|Statistics+Transform
VTK|FiltersTexture+FiltersParallel+ImagingStencil+ImagingMath+InteractionStyle+RenderingOpenGL2+RenderingVolumeOpenGL2+RenderingFreeType+RenderingLabel+InteractionWidgets+IOGeometry+IOXML
PRIVATE
- ITK|IOBioRad+IOBMP+IOBruker+IOCSV+IOGDCM+IOGE+IOGIPL+IOHDF5+IOIPL+IOJPEG+IOLSM+IOMesh+IOMeta+IOMINC+IOMRC+IONIFTI+IONRRD+IOPNG+IOSiemens+IOSpatialObjects+IOStimulate+IOTIFF+IOTransformBase+IOTransformHDF5+IOTransformInsightLegacy+IOTransformMatlab+IOVTK+IOXML
+ ITK|IOBioRad+IOBMP+IOBruker+IOCSV+IOGDCM+IOGE+IOGIPL+IOHDF5+IOIPL+IOJPEG+IOJPEG2000+IOLSM+IOMesh+IOMeta+IOMINC+IOMRC+IONIFTI+IONRRD+IOPNG+IOSiemens+IOSpatialObjects+IOStimulate+IOTIFF+IOTransformBase+IOTransformHDF5+IOTransformInsightLegacy+IOTransformMatlab+IOVTK+IOXML
tinyxml2
${optional_private_package_depends}
# Do not automatically create CppMicroServices initialization code.
# Because the VTK "auto-init" functionality injects file-local static
# initialization code in every cpp file which includes a VTK header,
# static initialization order becomes an issue again. For the Mitk
# core library, we need to ensure that the VTK static initialization stuff
# happens before the CppMicroServices initialization, since the latter
# might already use VTK code which needs to access VTK object factories.
# Hence, CppMicroServices initialization code is placed manually within
# the mitkCoreActivator.cpp file.
NO_INIT
)
if(NOT TARGET ${MODULE_TARGET})
message(SEND_ERROR "Core target ${MODULE_TARGET} does not exist")
endif()
function(_itk_create_factory_register_manager)
# In MITK_ITK_Config.cmake, we do *not* include ITK_USE_FILE, which
# prevents multiple registrations/unregistrations of ITK IO factories
# during library loading/unloading (of MITK libraries). However, we need
# "one" place where the IO factories are registered at
# least once. This could be the application executable, but every executable would
# need to take care of that itself. Instead, we allow the auto registration in the
# Mitk Core library.
set(NO_DIRECTORY_SCOPED_ITK_COMPILE_DEFINITION 1)
find_package(ITK)
include(${ITK_USE_FILE})
if(NOT ITK_NO_IO_FACTORY_REGISTER_MANAGER)
# We manually add the define which will be of target scope. MITK
# patches ITK_USE_FILE to remove the directory scoped compile
# definition since it would be propagated to other targets in the
# same directory scope but these targets might want to *not*
# use the ITK factory manager stuff.
target_compile_definitions(${MODULE_TARGET} PRIVATE ITK_IO_FACTORY_REGISTER_MANAGER)
endif()
endfunction()
_itk_create_factory_register_manager()
if(BUILD_TESTING)
add_subdirectory(TestingHelper)
add_subdirectory(test)
endif()
diff --git a/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp b/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp
index 80819797ea..09594900e3 100644
--- a/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp
+++ b/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp
@@ -1,239 +1,239 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// VTK
#include <vtkCamera.h>
#include <vtkOpenGLRenderWindow.h>
#include <vtkPNGWriter.h>
#include <vtkRenderLargeImage.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
// MITK
#include <mitkNodePredicateDataType.h>
#include <mitkRenderWindow.h>
#include <mitkRenderingTestHelper.h>
#include <mitkSliceNavigationController.h>
#include <mitkStandaloneDataStorage.h>
#include <mitkException.h>
#include <mitkTestNotRunException.h>
#include <mitkTestingMacros.h>
// VTK Testing to compare the rendered image pixel-wise against a reference screen shot
#include "vtkTesting.h"
mitk::RenderingTestHelper::RenderingTestHelper(int width,
int height,
AntiAliasing antiAliasing)
: m_AutomaticallyCloseRenderWindow(true)
{
this->Initialize(width, height, antiAliasing);
}
mitk::RenderingTestHelper::RenderingTestHelper(
int width, int height, int argc, char *argv[], AntiAliasing antiAliasing)
: m_AutomaticallyCloseRenderWindow(true)
{
this->Initialize(width, height, antiAliasing);
this->SetInputFileNames(argc, argv);
}
void mitk::RenderingTestHelper::Initialize(int width, int height, AntiAliasing antiAliasing)
{
RenderingManager::GetInstance()->SetAntiAliasing(antiAliasing);
mitk::UIDGenerator uidGen = mitk::UIDGenerator("UnnamedRenderer_");
m_RenderWindow = mitk::RenderWindow::New(nullptr, uidGen.GetUID().c_str());
auto renderWindow = m_RenderWindow->GetVtkRenderWindow();
if (0 == renderWindow->SupportsOpenGL())
{
auto openGLRenderWindow = dynamic_cast<vtkOpenGLRenderWindow*>(renderWindow);
auto message = nullptr != openGLRenderWindow
? openGLRenderWindow->GetOpenGLSupportMessage()
: std::string("No details available.");
mitkThrowException(mitk::TestNotRunException) << "OpenGL not supported: " << message;
}
m_DataStorage = mitk::StandaloneDataStorage::New();
m_RenderWindow->GetRenderer()->SetDataStorage(m_DataStorage);
this->SetMapperIDToRender2D();
this->GetVtkRenderWindow()->SetSize(width, height);
m_RenderWindow->GetRenderer()->Resize(width, height);
}
mitk::RenderingTestHelper::~RenderingTestHelper()
{
}
void mitk::RenderingTestHelper::SetMapperID(mitk::BaseRenderer::StandardMapperSlot id)
{
m_RenderWindow->GetRenderer()->SetMapperID(id);
}
void mitk::RenderingTestHelper::SetMapperIDToRender3D()
{
this->SetMapperID(mitk::BaseRenderer::Standard3D);
mitk::RenderingManager::GetInstance()->InitializeViews(
this->GetDataStorage()->ComputeBoundingGeometry3D(this->GetDataStorage()->GetAll()));
}
void mitk::RenderingTestHelper::SetMapperIDToRender2D()
{
this->SetMapperID(mitk::BaseRenderer::Standard2D);
}
void mitk::RenderingTestHelper::Render()
{
// if the datastorage is initialized and at least 1 image is loaded render it
- if (m_DataStorage.IsNotNull() || m_DataStorage->GetAll()->Size() >= 1)
+ if (m_DataStorage.IsNotNull() && m_DataStorage->GetAll()->Size() >= 1)
{
// Prepare the VTK camera before rendering.
m_RenderWindow->GetRenderer()->PrepareRender();
this->GetVtkRenderWindow()->Render();
this->GetVtkRenderWindow()->WaitForCompletion();
if (m_AutomaticallyCloseRenderWindow == false)
{
// Use interaction to stop the test
this->GetVtkRenderWindow()->GetInteractor()->Start();
}
}
else
{
MITK_ERROR << "No images loaded in data storage!";
}
}
mitk::DataStorage::Pointer mitk::RenderingTestHelper::GetDataStorage()
{
return m_DataStorage;
}
void mitk::RenderingTestHelper::SetInputFileNames(int argc, char *argv[])
{
// i is set 1, because 0 is the testname as string
// parse parameters
for (int i = 1; i < argc; ++i)
{
// add everything to a list but -T and -V
std::string tmp = argv[i];
if ((tmp.compare("-T")) && (tmp.compare("-V")))
{
this->AddToStorage(tmp);
}
else
{
break;
}
}
}
void mitk::RenderingTestHelper::SetViewDirection(mitk::SliceNavigationController::ViewDirection viewDirection)
{
mitk::BaseRenderer::GetInstance(m_RenderWindow->GetVtkRenderWindow())
->GetSliceNavigationController()
->SetDefaultViewDirection(viewDirection);
mitk::RenderingManager::GetInstance()->InitializeViews(
m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll()));
}
void mitk::RenderingTestHelper::ReorientSlices(mitk::Point3D origin, mitk::Vector3D rotation)
{
mitk::SliceNavigationController::Pointer sliceNavigationController =
mitk::BaseRenderer::GetInstance(m_RenderWindow->GetVtkRenderWindow())->GetSliceNavigationController();
sliceNavigationController->ReorientSlices(origin, rotation);
}
vtkRenderer *mitk::RenderingTestHelper::GetVtkRenderer()
{
return m_RenderWindow->GetRenderer()->GetVtkRenderer();
}
void mitk::RenderingTestHelper::SetImageProperty(const char *propertyKey, mitk::BaseProperty *property)
{
this->m_DataStorage->GetNode(mitk::NodePredicateDataType::New("Image"))->SetProperty(propertyKey, property);
}
vtkRenderWindow *mitk::RenderingTestHelper::GetVtkRenderWindow()
{
return m_RenderWindow->GetVtkRenderWindow();
}
bool mitk::RenderingTestHelper::CompareRenderWindowAgainstReference(int argc, char *argv[], double threshold)
{
this->Render();
// retVal meanings: (see VTK/Rendering/vtkTesting.h)
// 0 = test failed
// 1 = test passed
// 2 = test not run
// 3 = something with vtkInteraction
if (vtkTesting::Test(argc, argv, this->GetVtkRenderWindow(), threshold) == 1)
return true;
else
return false;
}
// method to save a screenshot of the renderwindow (e.g. create a reference screenshot)
void mitk::RenderingTestHelper::SaveAsPNG(std::string fileName)
{
vtkSmartPointer<vtkRenderer> renderer = this->GetVtkRenderer();
bool doubleBuffering(renderer->GetRenderWindow()->GetDoubleBuffer());
renderer->GetRenderWindow()->DoubleBufferOff();
vtkSmartPointer<vtkRenderLargeImage> magnifier = vtkSmartPointer<vtkRenderLargeImage>::New();
magnifier->SetInput(renderer);
magnifier->SetMagnification(1);
vtkSmartPointer<vtkImageWriter> fileWriter = vtkSmartPointer<vtkPNGWriter>::New();
fileWriter->SetInputConnection(magnifier->GetOutputPort());
fileWriter->SetFileName(fileName.c_str());
fileWriter->Write();
renderer->GetRenderWindow()->SetDoubleBuffer(doubleBuffering);
}
void mitk::RenderingTestHelper::SetAutomaticallyCloseRenderWindow(bool automaticallyCloseRenderWindow)
{
m_AutomaticallyCloseRenderWindow = automaticallyCloseRenderWindow;
}
void mitk::RenderingTestHelper::SaveReferenceScreenShot(std::string fileName)
{
this->SaveAsPNG(fileName);
}
void mitk::RenderingTestHelper::AddToStorage(const std::string &filename)
{
try
{
mitk::IOUtil::Load(filename, *m_DataStorage.GetPointer());
mitk::RenderingManager::GetInstance()->InitializeViews(
m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll()));
}
catch ( const itk::ExceptionObject &e )
{
MITK_ERROR << "Failed loading test data '" << filename << "': " << e.what();
}
}
void mitk::RenderingTestHelper::AddNodeToStorage(mitk::DataNode::Pointer node)
{
this->m_DataStorage->Add(node);
mitk::RenderingManager::GetInstance()->InitializeViews(
m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll()));
}
diff --git a/Modules/Core/files.cmake b/Modules/Core/files.cmake
index fd48d9ca2c..3637647f25 100644
--- a/Modules/Core/files.cmake
+++ b/Modules/Core/files.cmake
@@ -1,326 +1,328 @@
file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*")
set(CPP_FILES
mitkCoreActivator.cpp
mitkCoreObjectFactoryBase.cpp
mitkCoreObjectFactory.cpp
mitkCoreServices.cpp
mitkException.cpp
Algorithms/mitkBaseDataSource.cpp
Algorithms/mitkClippedSurfaceBoundsCalculator.cpp
Algorithms/mitkCompareImageDataFilter.cpp
Algorithms/mitkCompositePixelValueToString.cpp
Algorithms/mitkConvert2Dto3DImageFilter.cpp
Algorithms/mitkDataNodeSource.cpp
Algorithms/mitkExtractSliceFilter.cpp
Algorithms/mitkExtractSliceFilter2.cpp
Algorithms/mitkHistogramGenerator.cpp
Algorithms/mitkImageChannelSelector.cpp
Algorithms/mitkImageSliceSelector.cpp
Algorithms/mitkImageSource.cpp
Algorithms/mitkImageTimeSelector.cpp
Algorithms/mitkImageToImageFilter.cpp
Algorithms/mitkImageToSurfaceFilter.cpp
Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp
Algorithms/mitkPlaneGeometryDataToSurfaceFilter.cpp
Algorithms/mitkPointSetSource.cpp
Algorithms/mitkPointSetToPointSetFilter.cpp
Algorithms/mitkRGBToRGBACastImageFilter.cpp
Algorithms/mitkSubImageSelector.cpp
Algorithms/mitkSurfaceSource.cpp
Algorithms/mitkSurfaceToImageFilter.cpp
Algorithms/mitkSurfaceToSurfaceFilter.cpp
Algorithms/mitkUIDGenerator.cpp
Algorithms/mitkVolumeCalculator.cpp
Algorithms/mitkTemporalJoinImagesFilter.cpp
Controllers/mitkBaseController.cpp
Controllers/mitkCallbackFromGUIThread.cpp
Controllers/mitkCameraController.cpp
Controllers/mitkCameraRotationController.cpp
Controllers/mitkLimitedLinearUndo.cpp
Controllers/mitkOperationEvent.cpp
Controllers/mitkPlanePositionManager.cpp
Controllers/mitkProgressBar.cpp
Controllers/mitkRenderingManager.cpp
Controllers/mitkSliceNavigationController.cpp
Controllers/mitkSlicesCoordinator.cpp
Controllers/mitkStatusBar.cpp
Controllers/mitkStepper.cpp
Controllers/mitkTestManager.cpp
Controllers/mitkUndoController.cpp
Controllers/mitkVerboseLimitedLinearUndo.cpp
Controllers/mitkVtkLayerController.cpp
+
DataManagement/mitkAnatomicalStructureColorPresets.cpp
DataManagement/mitkArbitraryTimeGeometry.cpp
DataManagement/mitkAbstractTransformGeometry.cpp
DataManagement/mitkAnnotationProperty.cpp
DataManagement/mitkApplicationCursor.cpp
DataManagement/mitkApplyTransformMatrixOperation.cpp
DataManagement/mitkBaseData.cpp
DataManagement/mitkBaseGeometry.cpp
DataManagement/mitkBaseProperty.cpp
DataManagement/mitkChannelDescriptor.cpp
DataManagement/mitkClippingProperty.cpp
DataManagement/mitkColorProperty.cpp
DataManagement/mitkDataNode.cpp
DataManagement/mitkDataStorage.cpp
DataManagement/mitkEnumerationProperty.cpp
DataManagement/mitkFloatPropertyExtension.cpp
DataManagement/mitkGeometry3D.cpp
DataManagement/mitkGeometryData.cpp
DataManagement/mitkGeometryTransformHolder.cpp
DataManagement/mitkGroupTagProperty.cpp
DataManagement/mitkGenericIDRelationRule.cpp
DataManagement/mitkIdentifiable.cpp
DataManagement/mitkImageAccessorBase.cpp
DataManagement/mitkImageCaster.cpp
DataManagement/mitkImageCastPart1.cpp
DataManagement/mitkImageCastPart2.cpp
DataManagement/mitkImageCastPart3.cpp
DataManagement/mitkImageCastPart4.cpp
DataManagement/mitkImage.cpp
DataManagement/mitkImageDataItem.cpp
DataManagement/mitkImageDescriptor.cpp
DataManagement/mitkImageReadAccessor.cpp
DataManagement/mitkImageStatisticsHolder.cpp
DataManagement/mitkImageVtkAccessor.cpp
DataManagement/mitkImageVtkReadAccessor.cpp
DataManagement/mitkImageVtkWriteAccessor.cpp
DataManagement/mitkImageWriteAccessor.cpp
DataManagement/mitkIntPropertyExtension.cpp
DataManagement/mitkIPersistenceService.cpp
DataManagement/mitkIPropertyAliases.cpp
DataManagement/mitkIPropertyDescriptions.cpp
DataManagement/mitkIPropertyExtensions.cpp
DataManagement/mitkIPropertyFilters.cpp
DataManagement/mitkIPropertyOwner.cpp
DataManagement/mitkIPropertyPersistence.cpp
DataManagement/mitkIPropertyProvider.cpp
DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp
DataManagement/mitkLandmarkProjector.cpp
DataManagement/mitkLevelWindow.cpp
DataManagement/mitkLevelWindowManager.cpp
DataManagement/mitkLevelWindowPreset.cpp
DataManagement/mitkLevelWindowProperty.cpp
DataManagement/mitkLine.cpp
DataManagement/mitkLookupTable.cpp
DataManagement/mitkLookupTableProperty.cpp
DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable
DataManagement/mitkMaterial.cpp
DataManagement/mitkMemoryUtilities.cpp
DataManagement/mitkModalityProperty.cpp
DataManagement/mitkModifiedLock.cpp
DataManagement/mitkNodePredicateAnd.cpp
DataManagement/mitkNodePredicateBase.cpp
DataManagement/mitkNodePredicateCompositeBase.cpp
DataManagement/mitkNodePredicateData.cpp
DataManagement/mitkNodePredicateDataType.cpp
DataManagement/mitkNodePredicateDataUID.cpp
DataManagement/mitkNodePredicateDimension.cpp
DataManagement/mitkNodePredicateFirstLevel.cpp
DataManagement/mitkNodePredicateFunction.cpp
DataManagement/mitkNodePredicateGeometry.cpp
DataManagement/mitkNodePredicateNot.cpp
DataManagement/mitkNodePredicateOr.cpp
DataManagement/mitkNodePredicateProperty.cpp
DataManagement/mitkNodePredicateDataProperty.cpp
DataManagement/mitkNodePredicateSource.cpp
DataManagement/mitkNodePredicateSubGeometry.cpp
DataManagement/mitkNumericConstants.cpp
DataManagement/mitkPlaneGeometry.cpp
DataManagement/mitkPlaneGeometryData.cpp
DataManagement/mitkPlaneOperation.cpp
DataManagement/mitkPlaneOrientationProperty.cpp
DataManagement/mitkPointOperation.cpp
DataManagement/mitkPointSet.cpp
DataManagement/mitkPointSetShapeProperty.cpp
DataManagement/mitkProperties.cpp
DataManagement/mitkPropertyAliases.cpp
DataManagement/mitkPropertyDescriptions.cpp
DataManagement/mitkPropertyExtension.cpp
DataManagement/mitkPropertyExtensions.cpp
DataManagement/mitkPropertyFilter.cpp
DataManagement/mitkPropertyFilters.cpp
DataManagement/mitkPropertyKeyPath.cpp
DataManagement/mitkPropertyList.cpp
DataManagement/mitkPropertyListReplacedObserver.cpp
DataManagement/mitkPropertyNameHelper.cpp
DataManagement/mitkPropertyObserver.cpp
DataManagement/mitkPropertyPersistence.cpp
DataManagement/mitkPropertyPersistenceInfo.cpp
DataManagement/mitkPropertyRelationRuleBase.cpp
DataManagement/mitkProportionalTimeGeometry.cpp
DataManagement/mitkRenderingModeProperty.cpp
DataManagement/mitkResliceMethodProperty.cpp
DataManagement/mitkRestorePlanePositionOperation.cpp
DataManagement/mitkRotationOperation.cpp
DataManagement/mitkScaleOperation.cpp
DataManagement/mitkSlicedData.cpp
DataManagement/mitkSlicedGeometry3D.cpp
DataManagement/mitkSmartPointerProperty.cpp
DataManagement/mitkStandaloneDataStorage.cpp
DataManagement/mitkStringProperty.cpp
DataManagement/mitkSurface.cpp
DataManagement/mitkSurfaceOperation.cpp
DataManagement/mitkSourceImageRelationRule.cpp
DataManagement/mitkThinPlateSplineCurvedGeometry.cpp
DataManagement/mitkTimeGeometry.cpp
DataManagement/mitkTransferFunction.cpp
DataManagement/mitkTransferFunctionInitializer.cpp
DataManagement/mitkTransferFunctionProperty.cpp
DataManagement/mitkTemporoSpatialStringProperty.cpp
DataManagement/mitkUIDManipulator.cpp
DataManagement/mitkVector.cpp
DataManagement/mitkVectorProperty.cpp
DataManagement/mitkVtkInterpolationProperty.cpp
DataManagement/mitkVtkRepresentationProperty.cpp
DataManagement/mitkVtkResliceInterpolationProperty.cpp
DataManagement/mitkVtkScalarModeProperty.cpp
DataManagement/mitkVtkVolumeRenderingProperty.cpp
DataManagement/mitkWeakPointerProperty.cpp
DataManagement/mitkIPropertyRelations.cpp
DataManagement/mitkPropertyRelations.cpp
Interactions/mitkAction.cpp
Interactions/mitkBindDispatcherInteractor.cpp
Interactions/mitkCrosshairPositionEvent.cpp
Interactions/mitkDataInteractor.cpp
Interactions/mitkDispatcher.cpp
Interactions/mitkDisplayActionEventBroadcast.cpp
Interactions/mitkDisplayActionEventFunctions.cpp
Interactions/mitkDisplayActionEventHandler.cpp
Interactions/mitkDisplayActionEventHandlerDesynchronized.cpp
Interactions/mitkDisplayActionEventHandlerStd.cpp
Interactions/mitkDisplayActionEventHandlerSynchronized.cpp
Interactions/mitkDisplayCoordinateOperation.cpp
Interactions/mitkDisplayInteractor.cpp
Interactions/mitkEventConfig.cpp
Interactions/mitkEventFactory.cpp
Interactions/mitkEventRecorder.cpp
Interactions/mitkEventStateMachine.cpp
Interactions/mitkInteractionEventConst.cpp
Interactions/mitkInteractionEvent.cpp
Interactions/mitkInteractionEventHandler.cpp
Interactions/mitkInteractionEventObserver.cpp
Interactions/mitkInteractionKeyEvent.cpp
Interactions/mitkInteractionPositionEvent.cpp
Interactions/mitkInteractionSchemeSwitcher.cpp
Interactions/mitkInternalEvent.cpp
Interactions/mitkMouseDoubleClickEvent.cpp
Interactions/mitkMouseMoveEvent.cpp
Interactions/mitkMousePressEvent.cpp
Interactions/mitkMouseReleaseEvent.cpp
Interactions/mitkMouseWheelEvent.cpp
Interactions/mitkPointSetDataInteractor.cpp
Interactions/mitkSinglePointDataInteractor.cpp
Interactions/mitkStateMachineAction.cpp
Interactions/mitkStateMachineCondition.cpp
Interactions/mitkStateMachineContainer.cpp
Interactions/mitkStateMachineState.cpp
Interactions/mitkStateMachineTransition.cpp
Interactions/mitkVtkEventAdapter.cpp
Interactions/mitkVtkInteractorStyle.cxx
Interactions/mitkXML2EventParser.cpp
IO/mitkAbstractFileIO.cpp
IO/mitkAbstractFileReader.cpp
IO/mitkAbstractFileWriter.cpp
IO/mitkCustomMimeType.cpp
IO/mitkFileReader.cpp
IO/mitkFileReaderRegistry.cpp
IO/mitkFileReaderSelector.cpp
IO/mitkFileReaderWriterBase.cpp
IO/mitkFileWriter.cpp
IO/mitkFileWriterRegistry.cpp
IO/mitkFileWriterSelector.cpp
IO/mitkGeometry3DToXML.cpp
IO/mitkIFileIO.cpp
IO/mitkIFileReader.cpp
IO/mitkIFileWriter.cpp
IO/mitkGeometryDataReaderService.cpp
IO/mitkGeometryDataWriterService.cpp
IO/mitkImageGenerator.cpp
IO/mitkImageVtkLegacyIO.cpp
IO/mitkImageVtkXmlIO.cpp
IO/mitkIMimeTypeProvider.cpp
IO/mitkIOConstants.cpp
IO/mitkIOMimeTypes.cpp
IO/mitkIOUtil.cpp
IO/mitkItkImageIO.cpp
IO/mitkItkLoggingAdapter.cpp
IO/mitkLegacyFileReaderService.cpp
IO/mitkLegacyFileWriterService.cpp
IO/mitkLocaleSwitch.cpp
IO/mitkLog.cpp
IO/mitkMimeType.cpp
IO/mitkMimeTypeProvider.cpp
IO/mitkOperation.cpp
IO/mitkPixelType.cpp
IO/mitkPointSetReaderService.cpp
IO/mitkPointSetWriterService.cpp
IO/mitkProportionalTimeGeometryToXML.cpp
IO/mitkRawImageFileReader.cpp
IO/mitkStandardFileLocations.cpp
IO/mitkSurfaceStlIO.cpp
IO/mitkSurfaceVtkIO.cpp
IO/mitkSurfaceVtkLegacyIO.cpp
IO/mitkSurfaceVtkXmlIO.cpp
+ IO/mitkUtf8Util.cpp
IO/mitkVtkLoggingAdapter.cpp
IO/mitkPreferenceListReaderOptionsFunctor.cpp
IO/mitkIOMetaInformationPropertyConstants.cpp
Rendering/mitkAbstractAnnotationRenderer.cpp
Rendering/mitkAnnotationUtils.cpp
Rendering/mitkBaseRenderer.cpp
#Rendering/mitkGLMapper.cpp Moved to deprecated LegacyGL Module
Rendering/mitkGradientBackground.cpp
Rendering/mitkImageVtkMapper2D.cpp
Rendering/mitkMapper.cpp
Rendering/mitkAnnotation.cpp
Rendering/mitkPlaneGeometryDataMapper2D.cpp
Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp
Rendering/mitkPointSetVtkMapper2D.cpp
Rendering/mitkPointSetVtkMapper3D.cpp
Rendering/mitkRenderWindowBase.cpp
Rendering/mitkRenderWindow.cpp
Rendering/mitkRenderWindowFrame.cpp
#Rendering/mitkSurfaceGLMapper2D.cpp Moved to deprecated LegacyGL Module
Rendering/mitkSurfaceVtkMapper2D.cpp
Rendering/mitkSurfaceVtkMapper3D.cpp
Rendering/mitkVtkEventProvider.cpp
Rendering/mitkVtkMapper.cpp
Rendering/mitkVtkPropRenderer.cpp
Rendering/mitkVtkWidgetRendering.cpp
Rendering/vtkMitkLevelWindowFilter.cpp
Rendering/vtkMitkRectangleProp.cpp
Rendering/vtkMitkRenderProp.cpp
Rendering/vtkMitkThickSlicesFilter.cpp
Rendering/vtkNeverTranslucentTexture.cpp
)
set(RESOURCE_FILES
Interactions/globalConfig.xml
Interactions/DisplayInteraction.xml
Interactions/DisplayConfig.xml
Interactions/DisplayConfigMITKBase.xml
Interactions/DisplayConfigPACSBase.xml
Interactions/DisplayConfigCrosshair.xml
Interactions/DisplayConfigRotation.xml
Interactions/DisplayConfigActivateCoupling.xml
Interactions/DisplayConfigSwivel.xml
Interactions/DisplayConfigPACSPan.xml
Interactions/DisplayConfigPACSScroll.xml
Interactions/DisplayConfigPACSZoom.xml
Interactions/DisplayConfigPACSLevelWindow.xml
Interactions/DisplayConfigMITKLimited.xml
Interactions/DisplayConfigBlockLMB.xml
Interactions/PointSet.xml
Interactions/Legacy/StateMachine.xml
Interactions/Legacy/DisplayConfigMITKTools.xml
Interactions/PointSetConfig.xml
mitkLevelWindowPresets.xml
mitkAnatomicalStructureColorPresets.xml
)
diff --git a/Modules/Core/include/itkImportMitkImageContainer.h b/Modules/Core/include/itkImportMitkImageContainer.h
index 59f79e4ab4..1343566f22 100644
--- a/Modules/Core/include/itkImportMitkImageContainer.h
+++ b/Modules/Core/include/itkImportMitkImageContainer.h
@@ -1,102 +1,98 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef __itkImportMitkImageContainer_h
#define __itkImportMitkImageContainer_h
#include <itkImportImageContainer.h>
#include <mitkImageAccessorBase.h>
#include <mitkImageDataItem.h>
namespace itk
{
/** \class ImportMitkImageContainer
* Defines an itk::Image front-end to an mitk::Image. This container
* conforms to the ImageContainerInterface. This is a full-fleged Object,
* so there is modification time, debug, and reference count information.
*
* Template parameters for ImportMitkImageContainer:
*
* TElementIdentifier =
* An INTEGRAL type for use in indexing the imported buffer.
*
* TElement =
* The element type stored in the container.
*/
template <typename TElementIdentifier, typename TElement>
class ImportMitkImageContainer : public ImportImageContainer<TElementIdentifier, TElement>
{
public:
/** Standard class typedefs. */
typedef ImportMitkImageContainer Self;
typedef Object Superclass;
typedef SmartPointer<Self> Pointer;
typedef SmartPointer<const Self> ConstPointer;
/** Save the template parameters. */
typedef TElementIdentifier ElementIdentifier;
typedef TElement Element;
/** Method for creation through the object factory. */
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** Standard part of every itk Object. */
itkTypeMacro(ImportMitkImageContainer, ImportImageContainer);
///** Get the pointer from which the image data is imported. */
// TElement *GetImportPointer() {return m_ImportPointer;};
/** \brief Set the mitk::ImageDataItem to be imported */
// void SetImageDataItem(mitk::ImageDataItem* imageDataItem);
void SetImageAccessor(mitk::ImageAccessorBase *imageAccess, size_t noBytes);
protected:
ImportMitkImageContainer();
~ImportMitkImageContainer() override;
/** PrintSelf routine. Normally this is a protected internal method. It is
* made public here so that Image can call this method. Users should not
* call this method but should call Print() instead. */
void PrintSelf(std::ostream &os, Indent indent) const override;
private:
ImportMitkImageContainer(const Self &); // purposely not implemented
void operator=(const Self &); // purposely not implemented
// mitk::ImageDataItem::Pointer m_ImageDataItem;
mitk::ImageAccessorBase *m_imageAccess;
};
} // end namespace itk
// Define instantiation macro for this template.
#define ITK_TEMPLATE_ImportMitkImageContainer(_, EXPORT, x, y) \
namespace itk \
{ \
_(2(class EXPORT ImportMitkImageContainer<ITK_TEMPLATE_2 x>)) \
namespace Templates \
{ \
typedef ImportMitkImageContainer<ITK_TEMPLATE_2 x> ImportMitkImageContainer##y; \
} \
}
-//#if ITK_TEMPLATE_EXPLICIT
-//# include "Templates/itkImportMitkImageContainer+-.h"
-//#endif
-
-#if ITK_TEMPLATE_TXX
+#ifndef ITK_MANUAL_INSTANTIATION
#include "itkImportMitkImageContainer.txx"
#endif
#endif
diff --git a/Modules/Core/include/itkVtkAbstractTransform.h b/Modules/Core/include/itkVtkAbstractTransform.h
index 54ff4715b7..3a241fe832 100644
--- a/Modules/Core/include/itkVtkAbstractTransform.h
+++ b/Modules/Core/include/itkVtkAbstractTransform.h
@@ -1,101 +1,102 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKVTKABSTRACTTRANSFORM_H_HEADER_INCLUDED_C1C68A2C
#define MITKVTKABSTRACTTRANSFORM_H_HEADER_INCLUDED_C1C68A2C
#include "itkTransform.h"
#include <MitkCoreExports.h>
class vtkAbstractTransform;
namespace itk
{
//##Documentation
//## @brief Adapter from vtkAbstractTransform to itk::Transform<TScalarType, 3, 3>
//## @ingroup Geometry
template <class TScalarType>
class VtkAbstractTransform : public itk::Transform<TScalarType, 3, 3>
{
public:
typedef VtkAbstractTransform Self;
typedef Transform<TScalarType, 3, 3> Superclass;
typedef SmartPointer<Self> Pointer;
typedef SmartPointer<const Self> ConstPointer;
typedef typename Superclass::OutputPointType OutputPointType;
typedef typename Superclass::OutputVectorType OutputVectorType;
typedef typename Superclass::OutputVnlVectorType OutputVnlVectorType;
typedef typename Superclass::OutputCovariantVectorType OutputCovariantVectorType;
typedef typename Superclass::InputPointType InputPointType;
typedef typename Superclass::InputVectorType InputVectorType;
typedef typename Superclass::InputVnlVectorType InputVnlVectorType;
typedef typename Superclass::InputCovariantVectorType InputCovariantVectorType;
typedef typename Superclass::ParametersType ParametersType;
typedef typename Superclass::JacobianType JacobianType;
+ typedef typename Superclass::JacobianPositionType JacobianPositionType;
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
//##Documentation
//## @brief Get the vtkAbstractTransform (stored in m_VtkAbstractTransform)
virtual vtkAbstractTransform *GetVtkAbstractTransform() const;
//##Documentation
//## @brief Get the inverse vtkAbstractTransform (stored in m_InverseVtkAbstractTransform)
virtual vtkAbstractTransform *GetInverseVtkAbstractTransform() const;
//##Documentation
//## @brief Set the vtkAbstractTransform (stored in m_VtkAbstractTransform)
virtual void SetVtkAbstractTransform(vtkAbstractTransform *aVtkAbstractTransform);
using Superclass::TransformVector;
using Superclass::TransformCovariantVector;
OutputPointType TransformPoint(const InputPointType &) const override;
OutputVectorType TransformVector(const InputVectorType &) const override;
OutputVnlVectorType TransformVector(const InputVnlVectorType &) const override;
OutputCovariantVectorType TransformCovariantVector(const InputCovariantVectorType &) const override;
virtual InputPointType BackTransform(const OutputPointType &point) const;
virtual InputVectorType BackTransform(const OutputVectorType &vector) const;
virtual InputVnlVectorType BackTransform(const OutputVnlVectorType &vector) const;
virtual InputCovariantVectorType BackTransform(const OutputCovariantVectorType &vector) const;
void SetParameters(const ParametersType &) override;
void SetFixedParameters(const ParametersType &) override;
void ComputeJacobianWithRespectToParameters(const InputPointType &, JacobianType &) const override;
- void ComputeJacobianWithRespectToPosition(const InputPointType &, JacobianType &) const override;
+ void ComputeJacobianWithRespectToPosition(const InputPointType &, JacobianPositionType &) const override;
- unsigned long GetMTime() const override;
+ itk::ModifiedTimeType GetMTime() const override;
protected:
VtkAbstractTransform();
~VtkAbstractTransform() override;
//##Documentation
//## @brief Instance of the vtkAbstractTransform
vtkAbstractTransform *m_VtkAbstractTransform;
//##Documentation
//## @brief Instance of the vtkAbstractTransform
vtkAbstractTransform *m_InverseVtkAbstractTransform;
mutable unsigned long m_LastVtkAbstractTransformTimeStamp;
};
} // namespace itk
#ifndef MITK_MANUAL_INSTANTIATION
#include "itkVtkAbstractTransform.txx"
#endif
#endif /* MITKVTKABSTRACTTRANSFORM_H_HEADER_INCLUDED_C1C68A2C */
diff --git a/Modules/Core/include/itkVtkAbstractTransform.txx b/Modules/Core/include/itkVtkAbstractTransform.txx
index f00836fc51..6e6a87ee5c 100644
--- a/Modules/Core/include/itkVtkAbstractTransform.txx
+++ b/Modules/Core/include/itkVtkAbstractTransform.txx
@@ -1,236 +1,236 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "itkVtkAbstractTransform.h"
#include <mitkNumericTypes.h>
#include <vtkAbstractTransform.h>
namespace itk
{
template <class TScalarType>
itk::VtkAbstractTransform<TScalarType>::VtkAbstractTransform()
: m_VtkAbstractTransform(nullptr), m_InverseVtkAbstractTransform(nullptr), m_LastVtkAbstractTransformTimeStamp(0)
{
}
template <class TScalarType>
itk::VtkAbstractTransform<TScalarType>::~VtkAbstractTransform()
{
if (m_VtkAbstractTransform != nullptr)
m_VtkAbstractTransform->UnRegister(nullptr);
}
template <class TScalarType>
vtkAbstractTransform *itk::VtkAbstractTransform<TScalarType>::GetVtkAbstractTransform() const
{
return m_VtkAbstractTransform;
}
template <class TScalarType>
vtkAbstractTransform *itk::VtkAbstractTransform<TScalarType>::GetInverseVtkAbstractTransform() const
{
return m_InverseVtkAbstractTransform;
}
template <class TScalarType>
void itk::VtkAbstractTransform<TScalarType>::SetVtkAbstractTransform(vtkAbstractTransform *aVtkAbstractTransform)
{
if (m_VtkAbstractTransform == aVtkAbstractTransform)
return;
if (m_VtkAbstractTransform != nullptr)
m_VtkAbstractTransform->UnRegister(nullptr);
m_VtkAbstractTransform = aVtkAbstractTransform;
if (m_VtkAbstractTransform != nullptr)
{
m_VtkAbstractTransform->Register(nullptr);
m_InverseVtkAbstractTransform = m_VtkAbstractTransform->GetInverse(); // memory managed by m_VtkAbstractTransform
}
m_LastVtkAbstractTransformTimeStamp = m_VtkAbstractTransform->GetMTime();
this->Modified();
}
// Transform a point
template <class TScalarType>
typename itk::VtkAbstractTransform<TScalarType>::OutputPointType
itk::VtkAbstractTransform<TScalarType>::TransformPoint(const InputPointType &point) const
{
assert(m_VtkAbstractTransform != nullptr);
OutputPointType outputpoint;
vnl_vector<TScalarType> vnl_vec;
mitk::ScalarType vtkpt[3];
mitk::itk2vtk(point, vtkpt);
m_VtkAbstractTransform->TransformPoint(vtkpt, vtkpt);
mitk::vtk2itk(vtkpt, outputpoint);
return outputpoint;
}
// Transform a vector
template <class TScalarType>
typename itk::VtkAbstractTransform<TScalarType>::OutputVectorType
itk::VtkAbstractTransform<TScalarType>::TransformVector(const InputVectorType &vect) const
{
assert(m_VtkAbstractTransform != nullptr);
OutputVectorType outputvector;
vnl_vector<TScalarType> vnl_vec;
mitk::ScalarType vtkpt[3] = {0, 0, 0};
mitk::ScalarType vtkvec[3];
mitk::vnl2vtk<TScalarType, mitk::ScalarType>(vect.GetVnlVector(), vtkvec);
m_VtkAbstractTransform->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec);
mitk::vtk2itk(vtkvec, outputvector);
return outputvector;
}
// Transform a vnl_vector_fixed
template <class TScalarType>
typename itk::VtkAbstractTransform<TScalarType>::OutputVnlVectorType
itk::VtkAbstractTransform<TScalarType>::TransformVector(const InputVnlVectorType &vect) const
{
assert(m_VtkAbstractTransform != nullptr);
OutputVnlVectorType outputvector;
mitk::ScalarType vtkpt[3] = {0, 0, 0};
mitk::ScalarType vtkvec[3];
mitk::vnl2vtk<TScalarType, mitk::ScalarType>(vect, vtkvec);
m_VtkAbstractTransform->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec);
mitk::vtk2itk(vtkvec, outputvector);
return outputvector;
}
// Transform a CovariantVector
template <class TScalarType>
typename itk::VtkAbstractTransform<TScalarType>::OutputCovariantVectorType
itk::VtkAbstractTransform<TScalarType>::TransformCovariantVector(const InputCovariantVectorType & /*vec*/) const
{
itkExceptionMacro(<< "implement before using!");
OutputCovariantVectorType result; // Converted vector
// for (unsigned int i = 0; i < NDimensions; i++)
// {
// result[i] = NumericTraits<mitk::ScalarType>::Zero;
// for (unsigned int j = 0; j < NDimensions; j++)
// {
// result[i] += m_Inverse[j][i]*vec[j]; // Inverse transposed
// }
// }
return result;
}
// Back transform a point
template <class TScalarType>
typename VtkAbstractTransform<TScalarType>::InputPointType itk::VtkAbstractTransform<TScalarType>::BackTransform(
const OutputPointType &point) const
{
assert(m_VtkAbstractTransform != nullptr);
OutputPointType outputpoint;
mitk::ScalarType vtkpt[3];
mitk::itk2vtk(point, vtkpt);
m_InverseVtkAbstractTransform->TransformPoint(vtkpt, vtkpt);
mitk::vtk2itk(vtkpt, outputpoint);
return outputpoint;
}
// Back transform a vector
template <class TScalarType>
typename VtkAbstractTransform<TScalarType>::InputVectorType itk::VtkAbstractTransform<TScalarType>::BackTransform(
const OutputVectorType &vect) const
{
assert(m_VtkAbstractTransform != nullptr);
OutputVectorType outputvector;
mitk::ScalarType vtkpt[3] = {0, 0, 0};
mitk::ScalarType vtkvec[3];
mitk::itk2vtk(vect, vtkvec);
m_InverseVtkAbstractTransform->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec);
mitk::vtk2itk(vtkvec, outputvector);
return outputvector;
}
// Back transform a vnl_vector
template <class TScalarType>
typename VtkAbstractTransform<TScalarType>::InputVnlVectorType itk::VtkAbstractTransform<TScalarType>::BackTransform(
const OutputVnlVectorType &vect) const
{
assert(m_InverseVtkAbstractTransform != nullptr);
OutputVnlVectorType outputvector;
mitk::ScalarType vtkpt[3] = {0, 0, 0};
mitk::ScalarType vtkvec[3];
mitk::itk2vtk(vect, vtkvec);
m_InverseVtkAbstractTransform->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec);
mitk::vtk2itk(vtkvec, outputvector);
return outputvector;
}
// Back Transform a CovariantVector
template <class TScalarType>
typename VtkAbstractTransform<TScalarType>::InputCovariantVectorType
itk::VtkAbstractTransform<TScalarType>::BackTransform(const OutputCovariantVectorType &vec) const
{
itkExceptionMacro(<< "implement before using!");
// for (unsigned int i = 0; i < NDimensions; i++)
// {
// result[i] = NumericTraits<mitk::ScalarType>::Zero;
// for (unsigned int j = 0; j < NDimensions; j++)
// {
// result[i] += m_Matrix[j][i]*vec[j]; // Direct matrix transposed
// }
// }
return vec;
}
template <class TScalarType>
- unsigned long itk::VtkAbstractTransform<TScalarType>::GetMTime() const
+ itk::ModifiedTimeType itk::VtkAbstractTransform<TScalarType>::GetMTime() const
{
if ((m_VtkAbstractTransform != nullptr) &&
(m_LastVtkAbstractTransformTimeStamp < m_VtkAbstractTransform->GetMTime()))
{
m_LastVtkAbstractTransformTimeStamp = m_VtkAbstractTransform->GetMTime();
this->Modified();
}
return Superclass::GetMTime();
}
template <class TScalarType>
void itk::VtkAbstractTransform<TScalarType>::SetParameters(const ParametersType &)
{
// TODO
}
template <class TScalarType>
void itk::VtkAbstractTransform<TScalarType>::SetFixedParameters(const ParametersType &)
{
// TODO
}
template <class TScalarType>
void itk::VtkAbstractTransform<TScalarType>::ComputeJacobianWithRespectToParameters(const InputPointType &,
JacobianType &) const
{
// TODO
}
template <class TScalarType>
void itk::VtkAbstractTransform<TScalarType>::ComputeJacobianWithRespectToPosition(const InputPointType &,
- JacobianType &) const
+ JacobianPositionType &) const
{
// TODO
}
} // namespace itk
diff --git a/Modules/Core/include/mitkAbstractTransformGeometry.h b/Modules/Core/include/mitkAbstractTransformGeometry.h
index 611fcf7524..17a706ec3a 100644
--- a/Modules/Core/include/mitkAbstractTransformGeometry.h
+++ b/Modules/Core/include/mitkAbstractTransformGeometry.h
@@ -1,234 +1,234 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKVTKABSTRACTTRANSFORMPLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C
#define MITKVTKABSTRACTTRANSFORMPLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C
#include "mitkPlaneGeometry.h"
#include <MitkCoreExports.h>
#include "itkVtkAbstractTransform.h"
class vtkAbstractTransform;
namespace mitk
{
//##Documentation
//## @brief Describes a geometry defined by an vtkAbstractTransform and a plane
//##
//## vtkAbstractTransform is the most general transform in vtk (superclass for
//## all vtk geometric transformations). It defines an arbitrary 3D transformation,
//## i.e., a transformation of 3D space into 3D space. In contrast,
//## AbstractTransformGeometry (since it is a subclass of PlaneGeometry) describes a
//## 2D manifold in 3D space. The 2D manifold is defined as the manifold that results
//## from transforming a rectangle (given in m_Plane as a PlaneGeometry) by the
//## vtkAbstractTransform (given in m_VtkAbstractTransform).
//## The PlaneGeometry m_Plane is used to define the parameter space. 2D coordinates are
//## first mapped by the PlaneGeometry and the resulting 3D coordinates are put into
//## the vtkAbstractTransform.
//## @note This class is the superclass of concrete geometries. Since there is no
//## write access to the vtkAbstractTransform and m_Plane, this class is somehow
//## abstract. For full write access from extern, use ExternAbstractTransformGeometry.
//## @note The bounds of the PlaneGeometry are used as the parametric bounds.
//## @sa ExternAbstractTransformGeometry
//## @ingroup Geometry
class MITKCORE_EXPORT AbstractTransformGeometry : public PlaneGeometry
{
public:
mitkClassMacro(AbstractTransformGeometry, PlaneGeometry);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
//##Documentation
//## @brief Get the vtkAbstractTransform (stored in m_VtkAbstractTransform)
virtual vtkAbstractTransform *GetVtkAbstractTransform() const;
- unsigned long GetMTime() const override;
+ itk::ModifiedTimeType GetMTime() const override;
//##Documentation
//## @brief Get the rectangular area that is used for transformation by
//## m_VtkAbstractTransform and therewith defines the 2D manifold described by
//## AbstractTransformGeometry
itkGetConstObjectMacro(Plane, PlaneGeometry);
/**
* \brief projects the given point onto the curved plane
*/
bool Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const override;
/**
* \brief projects a given vector starting from given point onto the curved plane
* \warning no satisfiyng implementation existing yet
*/
bool Project(const mitk::Point3D &atPt3d_mm,
const mitk::Vector3D &vec3d_mm,
mitk::Vector3D &projectedVec3d_mm) const override;
/**
* \brief projects a given vector starting from standard point onto the curved plane
* \warning no satisfying implementation existing yet
*/
bool Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const override;
bool Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const override;
void Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const override;
bool Map(const mitk::Point3D &atPt3d_mm,
const mitk::Vector3D &vec3d_mm,
mitk::Vector2D &vec2d_mm) const override;
void Map(const mitk::Point2D &atPt2d_mm,
const mitk::Vector2D &vec2d_mm,
mitk::Vector3D &vec3d_mm) const override;
void IndexToWorld(const mitk::Point2D &pt_units, mitk::Point2D &pt_mm) const override;
void WorldToIndex(const mitk::Point2D &pt_mm, mitk::Point2D &pt_units) const override;
//##Documentation
//## @brief Convert (continuous or discrete) index coordinates of a \em vector
//## \a vec_units to world coordinates (in mm)
//## @deprecated First parameter (Point2D) is not used. If possible, please use void IndexToWorld(const
// mitk::Vector2D& vec_units, mitk::Vector2D& vec_mm) const.
//## For further information about coordinates types, please see the Geometry documentation
void IndexToWorld(const mitk::Point2D &atPt2d_units,
const mitk::Vector2D &vec_units,
mitk::Vector2D &vec_mm) const override;
//##Documentation
//## @brief Convert (continuous or discrete) index coordinates of a \em vector
//## \a vec_units to world coordinates (in mm)
//## For further information about coordinates types, please see the Geometry documentation
void IndexToWorld(const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const override;
//##Documentation
//## @brief Convert world coordinates (in mm) of a \em vector
//## \a vec_mm to (continuous!) index coordinates.
//## @deprecated First parameter (Point2D) is not used. If possible, please use void WorldToIndex(const
// mitk::Vector2D& vec_mm, mitk::Vector2D& vec_units) const.
//## For further information about coordinates types, please see the Geometry documentation
void WorldToIndex(const mitk::Point2D &atPt2d_mm,
const mitk::Vector2D &vec_mm,
mitk::Vector2D &vec_units) const override;
//##Documentation
//## @brief Convert world coordinates (in mm) of a \em vector
//## \a vec_mm to (continuous!) index coordinates.
//## For further information about coordinates types, please see the Geometry documentation
void WorldToIndex(const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const override;
bool IsAbove(const Point3D &pt3d_mm, bool considerBoundingBox = false) const override;
virtual mitk::ScalarType GetParametricExtentInMM(int direction) const;
virtual const itk::Transform<mitk::ScalarType, 3, 3> *GetParametricTransform() const;
//##Documentation
//## @brief Change the parametric bounds to @a oversampling times
//## the bounds of m_Plane.
//##
//## The change is done once (immediately). Later changes of the bounds
//## of m_Plane will not influence the parametric bounds. (Consequently,
//## there is no method to get the oversampling.)
virtual void SetOversampling(mitk::ScalarType oversampling);
//##Documentation
//## @brief Calculates the standard part of a BaseGeometry
//## (IndexToWorldTransform and bounding box) around the
//## curved geometry. Has to be implemented in subclasses.
//##
//## \sa SetFrameGeometry
virtual void CalculateFrameGeometry();
//##Documentation
//## @brief Set the frame geometry which is used as the standard
//## part of an BaseGeometry (IndexToWorldTransform and bounding box)
//##
//## Maybe used as a hint within which the interpolation shall occur
//## by concrete sub-classes.
//## \sa CalculateFrameGeometry
virtual void SetFrameGeometry(const mitk::BaseGeometry *frameGeometry);
itk::LightObject::Pointer InternalClone() const override;
//##Documentation
//## @brief Get the parametric bounding-box
//##
//## See AbstractTransformGeometry for an example usage of this.
itkGetConstObjectMacro(ParametricBoundingBox, BoundingBox);
//##Documentation
//## @brief Get the parametric bounds
//##
//## See AbstractTransformGeometry for an example usage of this.
const BoundingBox::BoundsArrayType &GetParametricBounds() const;
//##Documentation
//## @brief Get the parametric extent
//##
//## See AbstractTransformGeometry for an example usage of this.
mitk::ScalarType GetParametricExtent(int direction) const;
protected:
AbstractTransformGeometry();
AbstractTransformGeometry(const AbstractTransformGeometry &other);
~AbstractTransformGeometry() override;
//##Documentation
//## @brief Set the vtkAbstractTransform (stored in m_VtkAbstractTransform)
//##
//## Protected in this class, made public in ExternAbstractTransformGeometry.
virtual void SetVtkAbstractTransform(vtkAbstractTransform *aVtkAbstractTransform);
//##Documentation
//## @brief Set the rectangular area that is used for transformation by
//## m_VtkAbstractTransform and therewith defines the 2D manifold described by
//## ExternAbstractTransformGeometry
//##
//## Protected in this class, made public in ExternAbstractTransformGeometry.
//## @note The bounds of the PlaneGeometry are used as the parametric bounds.
//## @note The PlaneGeometry is cloned, @em not linked/referenced.
virtual void SetPlane(const mitk::PlaneGeometry *aPlane);
//##Documentation
//## @brief The rectangular area that is used for transformation by
//## m_VtkAbstractTransform and therewith defines the 2D manifold described by
//## AbstractTransformGeometry.
mitk::PlaneGeometry::Pointer m_Plane;
itk::VtkAbstractTransform<ScalarType>::Pointer m_ItkVtkAbstractTransform;
mitk::BaseGeometry::Pointer m_FrameGeometry;
//##Documentation
//## @brief Set the parametric bounds
//##
//## Protected in this class, made public in some sub-classes, e.g.,
//## ExternAbstractTransformGeometry.
virtual void SetParametricBounds(const BoundingBox::BoundsArrayType &bounds);
mutable mitk::BoundingBox::Pointer m_ParametricBoundingBox;
//##Documentation
//## @brief PreSetSpacing
//##
//## These virtual function allows a different beahiour in subclasses.
//## Do implement them in every subclass of BaseGeometry. If not needed, use
//## {Superclass::PreSetSpacing();};
void PreSetSpacing(const mitk::Vector3D &aSpacing) override { Superclass::PreSetSpacing(aSpacing); };
};
} // namespace mitk
#endif /* MITKVTKABSTRACTTRANSFORMPLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C */
diff --git a/Modules/Core/include/mitkAffineTransform3D.h b/Modules/Core/include/mitkAffineTransform3D.h
index 8a12d47dd8..e6f535cd75 100644
--- a/Modules/Core/include/mitkAffineTransform3D.h
+++ b/Modules/Core/include/mitkAffineTransform3D.h
@@ -1,24 +1,24 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
-#ifndef MITKAFFINETRANSFORM3D_H_
-#define MITKAFFINETRANSFORM3D_H_
+#ifndef mitkAffineTransform3D_h
+#define mitkAffineTransform3D_h
-#include <itkAffineGeometryFrame.h>
-#include <mitkVector.h>
+#include <mitkNumericConstants.h>
+#include <itkScalableAffineTransform.h>
namespace mitk
{
- typedef itk::AffineGeometryFrame<ScalarType, 3>::TransformType AffineTransform3D;
+ using AffineTransform3D = itk::ScalableAffineTransform<ScalarType, 3>;
}
-#endif /* MITKAFFINETRANSFORM3D_H_ */
+#endif
diff --git a/Modules/Core/include/mitkArray.h b/Modules/Core/include/mitkArray.h
index 7709718692..c1b86bbd17 100644
--- a/Modules/Core/include/mitkArray.h
+++ b/Modules/Core/include/mitkArray.h
@@ -1,143 +1,142 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKARRAY_H_
#define MITKARRAY_H_
#include <itkFixedArray.h>
#include "mitkEqual.h"
#include "mitkNumericConstants.h"
namespace mitk
{
/**
* Methods to copy from itk::FixedArray s (like mitk::Vector and mitk::Point) into ArrayTypes and vice versa.
* ArrayTypes here are all types who implement operator[].
* The two templated methods were made free floating so you may specialize them
* for your concrete type.
*/
/**
* @brief Copies elements of an array to this Vector
* @param[in] array the array whose values will be copied into the Vector. Must be of a type which overrides the []
* operator
* @param[out] toArray the FixedArrray (e.g., mitk::Vector or mitk::Point) which should hold the elements of array.
* @attention array must be of dimension NVectorDimension!
* @attention this method implicitly converts between data types.
*/
template <typename ArrayType, typename TCoordRep, unsigned int NVectorDimension>
void FillArray(itk::FixedArray<TCoordRep, NVectorDimension> &toArray, const ArrayType &array)
{
- itk::FixedArray<TCoordRep, NVectorDimension> vectorOrPoint;
for (unsigned short int var = 0; var < NVectorDimension; ++var)
{
toArray[var] = array[var];
}
}
/**
* @brief Copies elements of an array to this Vector
* @param[in] array the array whose values will be copied into the Vector. Must be of a type which overrides the []
* operator
* @return the FixedArray (e.g., mitk::Vector or mitk::Point) which should hold the elements of array.
* @attention array must be of dimension NVectorDimension!
* @attention this method implicitly converts between data types.
*/
template <typename ArrayType, typename TCoordRep, unsigned int NVectorDimension>
itk::FixedArray<TCoordRep, NVectorDimension> FillArray(const ArrayType &array)
{
itk::FixedArray<TCoordRep, NVectorDimension> vectorOrPoint;
mitk::FillArray(vectorOrPoint, array);
return vectorOrPoint;
}
/**
* @brief Copies the elements of this into an array
* @param[in] vectorOrPoint the itk::FixedArray which shall be copied into the array. Can e.g. be of type
* mitk::Vector
* or mitk::Point
* @param[out] array the array which will hold the elements. Must be of a type which overrides the [] operator.
* @attention array must be of dimension NVectorDimension!
* @attention this method implicitly converts between data types.
*/
template <typename ArrayType, typename TCoordRep, unsigned int NVectorDimension>
void ToArray(ArrayType &array, const itk::FixedArray<TCoordRep, NVectorDimension> &vectorOrPoint)
{
for (unsigned short int var = 0; var < NVectorDimension; ++var)
{
array[var] = vectorOrPoint[var];
}
}
/**
* @brief Copies the elements of this into an array
* @param[in] vectorOrPoint the itk::FixedArray which shall be copied into the array. Can e.g. be of type
* mitk::Vector
* or mitk::Point
* @return the array which will hold the elements. Must be of a type which overrides the [] operator.
* @attention array must be of dimension NVectorDimension!
* @attention this method implicitly converts between data types.
*/
template <typename ArrayType, typename TCoordRep, unsigned int NVectorDimension>
ArrayType ToArray(const itk::FixedArray<TCoordRep, NVectorDimension> &vectorOrPoint)
{
ArrayType result;
mitk::ToArray(result, vectorOrPoint);
return result;
}
// The FillVector3D and FillVector4D methods are implemented for all common array types here
template <class Tout>
inline void FillVector3D(Tout &out, mitk::ScalarType x, mitk::ScalarType y, mitk::ScalarType z)
{
out[0] = x;
out[1] = y;
out[2] = z;
}
template <class Tout>
inline void FillVector4D(Tout &out, mitk::ScalarType x, mitk::ScalarType y, mitk::ScalarType z, mitk::ScalarType t)
{
out[0] = x;
out[1] = y;
out[2] = z;
out[3] = t;
}
/**
* Compares two ArrayTypes of size size.
* ArrayTypes are all Objects/Types who have a [] operator. Pay attention not to set size higher
* than the actual size of the ArrayType as this will lead to unexpected results.
*/
template <typename TArrayType1, typename TArrayType2>
inline bool EqualArray(
TArrayType1 &arrayType1, TArrayType2 &arrayType2, int size, ScalarType eps = mitk::eps, bool verbose = false)
{
bool isEqual = true;
for (int var = 0; var < size; ++var)
{
isEqual = isEqual && Equal(arrayType1[var], arrayType2[var], eps);
}
ConditionalOutputOfDifference(arrayType1, arrayType2, eps, verbose, isEqual);
return isEqual;
}
}
#endif /* MITKARRAY_H_ */
diff --git a/Modules/Core/include/mitkBaseData.h b/Modules/Core/include/mitkBaseData.h
index c43122796f..14ab1c1856 100644
--- a/Modules/Core/include/mitkBaseData.h
+++ b/Modules/Core/include/mitkBaseData.h
@@ -1,388 +1,388 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef BASEDATA_H_HEADER_INCLUDED_C1EBB6FA
#define BASEDATA_H_HEADER_INCLUDED_C1EBB6FA
#include <itkDataObject.h>
#include "mitkBaseProcess.h"
#include "mitkIdentifiable.h"
#include "mitkIPropertyOwner.h"
#include "mitkOperationActor.h"
#include "mitkPropertyList.h"
#include "mitkTimeGeometry.h"
#include <MitkCoreExports.h>
namespace mitk
{
// class BaseProcess;
//##Documentation
//## @brief Base of all data objects
//##
//## Base of all data objects, e.g., images, contours, surfaces etc. Inherits
//## from itk::DataObject and thus can be included in a pipeline.
//## Inherits also from OperationActor and can be used as a destination for Undo
//## @remark Some derived classes may support the persistence of the Identifiable UID.
//** but it is no guaranteed feature and also depends on the format the data is stored in
//** as not all formats support storing of meta information. Please check the documentation
//** of the IFileReader and IFileWriter classes to see if the ID-persistance is supported.
//** MITK SceneIO supports the UID persistance for all BaseData derived classes.
//## @ingroup Data
class MITKCORE_EXPORT BaseData
: public itk::DataObject, public OperationActor, public Identifiable, public IPropertyOwner
{
public:
mitkClassMacroItkParent(BaseData, itk::DataObject);
// IPropertyProvider
BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) const override;
std::vector<std::string> GetPropertyKeys(const std::string &contextName = "", bool includeDefaultContext = false) const override;
std::vector<std::string> GetPropertyContextNames() const override;
// IPropertyOwner
BaseProperty * GetNonConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) override;
void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override;
void RemoveProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override;
/**
* \brief Return the TimeGeometry of the data as const pointer.
*
* \warning No update will be called. Use GetUpdatedGeometry() if you cannot
* be sure that the geometry is up-to-date.
*
* Normally used in GenerateOutputInformation of subclasses of BaseProcess.
*/
const mitk::TimeGeometry *GetTimeGeometry() const
{
return m_TimeGeometry.GetPointer();
}
/**
* @brief Return the TimeGeometry of the data as pointer.
*
* \warning No update will be called. Use GetUpdatedGeometry() if you cannot
* be sure that the geometry is up-to-date.
*
* Normally used in GenerateOutputInformation of subclasses of BaseProcess.
*/
mitk::TimeGeometry *GetTimeGeometry() { return m_TimeGeometry.GetPointer(); }
/**
* @brief Return the TimeGeometry of the data.
*
* The method does not simply return the value of the m_TimeGeometry
* member. Before doing this, it makes sure that the TimeGeometry
* is up-to-date (by setting the update extent to largest possible and
* calling UpdateOutputInformation).
*/
const mitk::TimeGeometry *GetUpdatedTimeGeometry();
/**
* \brief Expands the TimeGeometry to a number of TimeSteps.
*
* The method expands the TimeGeometry to the given number of TimeSteps,
* filling newly created elements with empty geometries. Sub-classes should override
* this method to handle the elongation of their data vectors, too.
* Note that a shrinking is neither possible nor intended.
*/
virtual void Expand(unsigned int timeSteps);
/**
* \brief Return the BaseGeometry of the data at time \a t.
*
* The method does not simply return
* m_TimeGeometry->GetGeometry(t).
* Before doing this, it makes sure that the BaseGeometry is up-to-date
* (by setting the update extent appropriately and calling
* UpdateOutputInformation).
*
* @todo Appropriate setting of the update extent is missing.
*/
const mitk::BaseGeometry *GetUpdatedGeometry(int t = 0);
//##Documentation
//## @brief Return the geometry, which is a TimeGeometry, of the data
//## as non-const pointer.
//##
//## \warning No update will be called. Use GetUpdatedGeometry() if you cannot
//## be sure that the geometry is up-to-date.
//##
//## Normally used in GenerateOutputInformation of subclasses of BaseProcess.
mitk::BaseGeometry *GetGeometry(int t = 0) const
{
if (m_TimeGeometry.IsNull())
return nullptr;
return m_TimeGeometry->GetGeometryForTimeStep(t);
}
//##Documentation
//## @brief Update the information for this BaseData (the geometry in particular)
//## so that it can be used as an output of a BaseProcess.
//##
//## This method is used in the pipeline mechanism to propagate information and
//## initialize the meta data associated with a BaseData. Any implementation
//## of this method in a derived class is assumed to call its source's
//## BaseProcess::UpdateOutputInformation() which determines modified
//## times, LargestPossibleRegions, and any extra meta data like spacing,
//## origin, etc. Default implementation simply call's it's source's
//## UpdateOutputInformation().
//## \note Implementations of this methods in derived classes must take care
//## that the geometry is updated by calling
//## GetTimeGeometry()->UpdateInformation()
//## \em after calling its source's BaseProcess::UpdateOutputInformation().
void UpdateOutputInformation() override;
//##Documentation
//## @brief Set the RequestedRegion to the LargestPossibleRegion.
//##
//## This forces a filter to produce all of the output in one execution
//## (i.e. not streaming) on the next call to Update().
void SetRequestedRegionToLargestPossibleRegion() override = 0;
//##Documentation
//## @brief Determine whether the RequestedRegion is outside of the BufferedRegion.
//##
//## This method returns true if the RequestedRegion
//## is outside the BufferedRegion (true if at least one pixel is
//## outside). This is used by the pipeline mechanism to determine
//## whether a filter needs to re-execute in order to satisfy the
//## current request. If the current RequestedRegion is already
//## inside the BufferedRegion from the previous execution (and the
//## current filter is up to date), then a given filter does not need
//## to re-execute
bool RequestedRegionIsOutsideOfTheBufferedRegion() override = 0;
//##Documentation
//## @brief Verify that the RequestedRegion is within the LargestPossibleRegion.
//##
//## If the RequestedRegion is not within the LargestPossibleRegion,
//## then the filter cannot possibly satisfy the request. This method
//## returns true if the request can be satisfied (even if it will be
//## necessary to process the entire LargestPossibleRegion) and
//## returns false otherwise. This method is used by
//## PropagateRequestedRegion(). PropagateRequestedRegion() throws a
//## InvalidRequestedRegionError exception if the requested region is
//## not within the LargestPossibleRegion.
bool VerifyRequestedRegion() override = 0;
//##Documentation
//## @brief Copy information from the specified data set.
//##
//## This method is part of the pipeline execution model. By default, a
//## BaseProcess will copy meta-data from the first input to all of its
//## outputs. See ProcessObject::GenerateOutputInformation(). Each
//## subclass of DataObject is responsible for being able to copy
//## whatever meta-data it needs from another DataObject.
//## The default implementation of this method copies the time sliced geometry
//## and the property list of an object. If a subclass overrides this
//## method, it should always call its superclass' version.
void CopyInformation(const itk::DataObject *data) override;
//##Documentation
//## @brief Check whether the data has been initialized, i.e.,
//## at least the Geometry and other header data has been set
//##
//## \warning Set to \a true by default for compatibility reasons.
//## Set m_Initialized=false in constructors of sub-classes that
//## support distinction between initialized and uninitialized state.
virtual bool IsInitialized() const;
//##Documentation
//## @brief Calls ClearData() and InitializeEmpty();
//## \warning Only use in subclasses that reimplemented these methods.
//## Just calling Clear from BaseData will reset an object to a not initialized,
//## invalid state.
virtual void Clear();
//##Documentation
//## @brief Check whether object contains data (at
//## a specified time), e.g., a set of points may be empty
//##
//## \warning Returns IsInitialized()==false by default for
//## compatibility reasons. Override in sub-classes that
//## support distinction between empty/non-empty state.
virtual bool IsEmptyTimeStep(unsigned int t) const;
//##Documentation
//## @brief Check whether object contains data (at
//## least at one point in time), e.g., a set of points
//## may be empty
//##
//## \warning Returns IsInitialized()==false by default for
//## compatibility reasons. Override in sub-classes that
//## support distinction between empty/non-empty state.
virtual bool IsEmpty() const;
//##Documentation
//## @brief Set the requested region from this data object to match the requested
//## region of the data object passed in as a parameter.
//##
//## This method is implemented in the concrete subclasses of BaseData.
void SetRequestedRegion(const itk::DataObject *data) override = 0;
//##Documentation
//##@brief overwrite if the Data can be called by an Interactor (StateMachine).
//##
//## Empty by default. Overwrite and implement all the necessary operations here
//## and get the necessary information from the parameter operation.
void ExecuteOperation(Operation *operation) override;
/**
* \brief Set the BaseGeometry of the data, which will be referenced (not copied!).
* Assumes the data object has only 1 time step ( is a 3D object ) and creates a
* new TimeGeometry which saves the given BaseGeometry. If an TimeGeometry has already
* been set for the object, it will be replaced after calling this function.
*
* @warning This method will normally be called internally by the sub-class of BaseData
* during initialization.
* \sa SetClonedGeometry
*/
virtual void SetGeometry(BaseGeometry *aGeometry3D);
/**
* \brief Set the TimeGeometry of the data, which will be referenced (not copied!).
*
* @warning This method will normally be called internally by the sub-class of BaseData
* during initialization.
* \sa SetClonedTimeGeometry
*/
virtual void SetTimeGeometry(TimeGeometry *geometry);
/**
* \brief Set a clone of the provided Geometry as Geometry of the data.
* Assumes the data object has only 1 time step ( is a 3D object ) and
* creates a new TimeGeometry. If an TimeGeometry has already
* been set for the object, it will be replaced after calling this function.
*
* \sa SetGeometry
*/
virtual void SetClonedGeometry(const BaseGeometry *aGeometry3D);
/**
* \brief Set a clone of the provided TimeGeometry as TimeGeometry of the data.
*
* \sa SetGeometry
*/
virtual void SetClonedTimeGeometry(const TimeGeometry *geometry);
//##Documentation
//## @brief Set a clone of the provided geometry as BaseGeometry of a given time step.
//##
//## \sa SetGeometry
virtual void SetClonedGeometry(const BaseGeometry *aGeometry3D, unsigned int time);
//##Documentation
//## @brief Get the data's property list
//## @sa GetProperty
//## @sa m_PropertyList
mitk::PropertyList::Pointer GetPropertyList() const;
//##Documentation
//## @brief Set the data's property list
//## @sa SetProperty
//## @sa m_PropertyList
void SetPropertyList(PropertyList *propertyList);
//##Documentation
//## @brief Get the property (instance of BaseProperty) with key @a propertyKey from the PropertyList,
//## and set it to this, respectively;
//## @sa GetPropertyList
//## @sa m_PropertyList
//## @sa m_MapOfPropertyLists
mitk::BaseProperty::Pointer GetProperty(const char *propertyKey) const;
void SetProperty(const char *propertyKey, BaseProperty *property);
//##Documentation
//## @brief Convenience method for setting the origin of
//## the BaseGeometry instances of all time steps
//##
//## \warning Geometries contained in the BaseGeometry will
//## \em not be changed, e.g. in case the BaseGeometry is a
//## SlicedGeometry3D the origin will \em not be propagated
//## to the contained slices. The sub-class SlicedData
//## does this for the case that the SlicedGeometry3D is
//## evenly spaced.
virtual void SetOrigin(const Point3D &origin);
/** \brief Get the process object that generated this data object.
*
* If there is no process object, then the data object has
* been disconnected from the pipeline, or the data object
* was created manually. (Note: we cannot use the GetObjectMacro()
* defined in itkMacro because the mutual dependency of
* DataObject and ProcessObject causes compile problems. Also,
* a forward reference smart pointer is returned, not a smart pointer,
* because of the circular dependency between the process and data object.)
*
* GetSource() returns a SmartPointer and not a WeakPointer
* because it is assumed the code calling GetSource() wants to hold a
* long term reference to the source. */
itk::SmartPointer<mitk::BaseDataSource> GetSource() const;
//##Documentation
//## @brief Get the number of time steps from the TimeGeometry
//## As the base data has not a data vector given by itself, the number
//## of time steps is defined over the time sliced geometry. In sub classes,
//## a better implementation could be over the length of the data vector.
unsigned int GetTimeSteps() const { return m_TimeGeometry->CountTimeSteps(); }
//##Documentation
//## @brief Get the modified time of the last change of the contents
//## this data object or its geometry.
- unsigned long GetMTime() const override;
+ itk::ModifiedTimeType GetMTime() const override;
/**
* \sa itk::ProcessObject::Graft
*/
void Graft(const DataObject *) override;
protected:
BaseData();
BaseData(const BaseData &other);
~BaseData() override;
//##Documentation
//## \brief Initialize the TimeGeometry for a number of time steps.
//## The TimeGeometry is initialized empty and evenly timed.
//## In many cases it will be necessary to overwrite this in sub-classes.
virtual void InitializeTimeGeometry(unsigned int timeSteps = 1);
//##Documentation
//## @brief reset to non-initialized state, release memory
virtual void ClearData();
//##Documentation
//## @brief Pure virtual; Must be used in subclasses to get a data object to a
//## valid state. Should at least create one empty object and call
//## Superclass::InitializeTimeGeometry() to ensure an existing valid geometry
virtual void InitializeEmpty() {}
void PrintSelf(std::ostream &os, itk::Indent indent) const override;
bool m_LastRequestedRegionWasOutsideOfTheBufferedRegion;
mutable unsigned int m_SourceOutputIndexDuplicate;
bool m_Initialized;
private:
//##Documentation
//## @brief PropertyList, f.e. to hold pic-tags, tracking-data,..
//##
PropertyList::Pointer m_PropertyList;
TimeGeometry::Pointer m_TimeGeometry;
};
} // namespace mitk
#endif /* BASEDATA_H_HEADER_INCLUDED_C1EBB6FA */
diff --git a/Modules/Core/include/mitkBaseGeometry.h b/Modules/Core/include/mitkBaseGeometry.h
index bda3ebf7fc..040e5e8134 100644
--- a/Modules/Core/include/mitkBaseGeometry.h
+++ b/Modules/Core/include/mitkBaseGeometry.h
@@ -1,755 +1,752 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef BaseGeometry_H_HEADER_INCLUDED
#define BaseGeometry_H_HEADER_INCLUDED
#include "mitkOperationActor.h"
#include <MitkCoreExports.h>
#include <mitkCommon.h>
#include "itkScalableAffineTransform.h"
#include "mitkNumericTypes.h"
-#include <itkAffineGeometryFrame.h>
#include <itkBoundingBox.h>
#include <itkIndex.h>
#include <itkQuaternionRigidTransform.h>
#include <mitkAffineTransform3D.h>
#include <mitkGeometryTransformHolder.h>
#include <vtkTransform.h>
class vtkMatrix4x4;
class vtkMatrixToLinearTransform;
class vtkLinearTransform;
namespace mitk
{
//##Documentation
//## @brief Standard 3D-BoundingBox typedef
//##
//## Standard 3D-BoundingBox typedef to get rid of template arguments (3D, type).
typedef itk::BoundingBox<unsigned long, 3, ScalarType> BoundingBox;
//##Documentation
//## @brief Standard typedef for time-bounds
typedef itk::FixedArray<ScalarType, 2> TimeBounds;
typedef itk::FixedArray<ScalarType, 3> FixedArrayType;
- typedef itk::AffineGeometryFrame<ScalarType, 3> AffineGeometryFrame3D;
-
//##Documentation
//## @brief BaseGeometry Describes the geometry of a data object
//##
//## The class holds
//## \li a bounding box which is axes-parallel in intrinsic coordinates
//## (often integer indices of pixels), to be accessed by
//## GetBoundingBox()
//## \li a transform to convert intrinsic coordinates into a
//## world-coordinate system with coordinates in millimeters
//## and milliseconds (all are floating point values), to
//## be accessed by GetIndexToWorldTransform()
//## \li an origin and spacing to define the geometry
//##
//## BaseGeometry and its sub-classes allow converting between
//## intrinsic coordinates (called index or unit coordinates)
//## and world-coordinates (called world or mm coordinates),
//## e.g. WorldToIndex.
//## In case you need integer index coordinates, provide an
//## mitk::Index3D (or itk::Index) as target variable to
//## WorldToIndex, otherwise you will get a continuous index
//## (floating point values).
//##
//## An important sub-class is SlicedGeometry3D, which descibes
//## data objects consisting of slices, e.g., objects of type Image.
//## Conversions between world coordinates (in mm) and unit coordinates
//## (e.g., pixels in the case of an Image) can be performed.
//##
//## For more information on related classes, see \ref Geometry.
//##
//## BaseGeometry instances referring to an Image need a slightly
//## different definition of corners, see SetImageGeometry. This
//## is usualy automatically called by Image.
//##
//## BaseGeometry have to be initialized in the method GenerateOutputInformation()
//## of BaseProcess (or CopyInformation/ UpdateOutputInformation of BaseData,
//## if possible, e.g., by analyzing pic tags in Image) subclasses. See also
//## itk::ProcessObject::GenerateOutputInformation(),
//## itk::DataObject::CopyInformation() and
//## itk::DataObject::UpdateOutputInformation().
//##
//## At least, it can return the bounding box of the data object.
//##
//## The BaseGeometry class is an abstract class. The most simple implementation
//## is the sublass Geometry3D.
//##
//## Rule: everything is in mm (ms) if not stated otherwise.
//## @ingroup Geometry
class MITKCORE_EXPORT BaseGeometry : public itk::Object, public OperationActor
{
public:
mitkClassMacroItkParent(BaseGeometry, itk::Object);
itkCloneMacro(Self);
// ********************************** TypeDef **********************************
typedef GeometryTransformHolder::TransformType TransformType;
typedef itk::BoundingBox<unsigned long, 3, ScalarType> BoundingBoxType;
typedef BoundingBoxType::BoundsArrayType BoundsArrayType;
typedef BoundingBoxType::Pointer BoundingBoxPointer;
// ********************************** Origin, Spacing **********************************
//##Documentation
//## @brief Get the origin, e.g. the upper-left corner of the plane
const Point3D GetOrigin() const;
//##Documentation
//## @brief Set the origin, i.e. the upper-left corner of the plane
//##
void SetOrigin(const Point3D &origin);
//##Documentation
//## @brief Get the spacing (size of a pixel).
//##
const mitk::Vector3D GetSpacing() const;
//##Documentation
//## @brief Set the spacing (m_Spacing).
//##
//##The spacing is also changed in the IndexToWorldTransform.
void SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing = false);
//##Documentation
//## @brief Get the origin as VnlVector
//##
//## \sa GetOrigin
VnlVector GetOriginVnl() const;
// ********************************** other functions **********************************
//##Documentation
//## @brief Get the DICOM FrameOfReferenceID referring to the
//## used world coordinate system
itkGetConstMacro(FrameOfReferenceID, unsigned int);
//##Documentation
//## @brief Set the DICOM FrameOfReferenceID referring to the
//## used world coordinate system
itkSetMacro(FrameOfReferenceID, unsigned int);
itkGetConstMacro(IndexToWorldTransformLastModified, unsigned long);
//##Documentation
//## @brief Overload of function Modified() to prohibit several calls of Modified() using the ModifiedLock class.
//##
//## For the use of Modified(), see class ModifiedLock.
void Modified() const override;
friend class ModifiedLock;
//##Documentation
//## @brief Is this BaseGeometry in a state that is valid?
//##
//## This function returns always true in the BaseGeometry class. Other implementations are possible in subclasses.
virtual bool IsValid() const;
// ********************************** Initialize **********************************
//##Documentation
//## @brief Initialize the BaseGeometry
void Initialize();
void InitializeGeometry(Self *newGeometry) const;
// ********************************** Transformations Set/Get **********************************
//##Documentation
//## @brief Get the transformation used to convert from index
//## to world coordinates
mitk::AffineTransform3D *GetIndexToWorldTransform();
//##Documentation
//## @brief Get the transformation used to convert from index
//## to world coordinates
const mitk::AffineTransform3D *GetIndexToWorldTransform() const;
//## @brief Set the transformation used to convert from index
//## to world coordinates. The spacing of the new transform is
//## copied to m_spacing.
void SetIndexToWorldTransform(mitk::AffineTransform3D *transform);
//##Documentation
//## @brief Convenience method for setting the ITK transform
//## (m_IndexToWorldTransform) via an vtkMatrix4x4.The spacing of
//## the new transform is copied to m_spacing.
//## \sa SetIndexToWorldTransform
void SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4 *vtkmatrix);
//## @brief Set the transformation used to convert from index
//## to world coordinates.This function keeps the original spacing.
void SetIndexToWorldTransformWithoutChangingSpacing(mitk::AffineTransform3D *transform);
//##Documentation
//## @brief Convenience method for setting the ITK transform
//## (m_IndexToWorldTransform) via an vtkMatrix4x4. This function keeps the original spacing.
//## \sa SetIndexToWorldTransform
void SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkMatrix4x4 *vtkmatrix);
//## Get the Vtk Matrix which describes the transform.
vtkMatrix4x4 *GetVtkMatrix();
//##Documentation
//## @brief Get the m_IndexToWorldTransform as a vtkLinearTransform
vtkLinearTransform *GetVtkTransform() const;
//##Documentation
//## @brief Set the transform to identity, the spacing to 1 and origin to 0
//##
void SetIdentity();
// ********************************** Transformations **********************************
//##Documentation
//## @brief Compose new IndexToWorldTransform with a given transform.
//##
//## This method composes m_IndexToWorldTransform with another transform,
//## modifying self to be the composition of self and other.
//## If the argument pre is true, then other is precomposed with self;
//## that is, the resulting transformation consists of first applying
//## other to the source, followed by self. If pre is false or omitted,
//## then other is post-composed with self; that is the resulting
//## transformation consists of first applying self to the source,
//## followed by other.
//## This method also changes m_spacing.
void Compose(const TransformType *other, bool pre = false);
//##Documentation
//## @brief Compose new IndexToWorldTransform with a given vtkMatrix4x4.
//##
//## Converts the vtkMatrix4x4 into a itk-transform and calls the previous method.
void Compose(const vtkMatrix4x4 *vtkmatrix, bool pre = false);
//##Documentation
//## @brief Translate the origin by a vector
//##
void Translate(const Vector3D &vector);
//##Documentation
//##@brief executes affine operations (translate, rotate, scale)
void ExecuteOperation(Operation *operation) override;
//##Documentation
//## @brief Convert world coordinates (in mm) of a \em point to (continuous!) index coordinates
//## \warning If you need (discrete) integer index coordinates (e.g., for iterating easily over an image),
//## use WorldToIndex(const mitk::Point3D& pt_mm, itk::Index<VIndexDimension> &index).
//## For further information about coordinates types, please see the Geometry documentation
void WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const;
//##Documentation
//## @brief Convert world coordinates (in mm) of a \em vector
//## \a vec_mm to (continuous!) index coordinates.
//## For further information about coordinates types, please see the Geometry documentation
void WorldToIndex(const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const;
//##Documentation
//## @brief Convert world coordinates (in mm) of a \em point to (discrete!) index coordinates.
//## This method rounds to integer indices!
//## For further information about coordinates types, please see the Geometry documentation
template <unsigned int VIndexDimension>
void WorldToIndex(const mitk::Point3D &pt_mm, itk::Index<VIndexDimension> &index) const
{
typedef itk::Index<VIndexDimension> IndexType;
mitk::Point3D pt_units;
this->WorldToIndex(pt_mm, pt_units);
int i, dim = index.GetIndexDimension();
if (dim > 3)
{
index.Fill(0);
dim = 3;
}
for (i = 0; i < dim; ++i)
{
index[i] = itk::Math::RoundHalfIntegerUp<typename IndexType::IndexValueType>(pt_units[i]);
}
}
//##Documentation
//## @brief Convert (continuous or discrete) index coordinates of a \em vector
//## \a vec_units to world coordinates (in mm)
//## For further information about coordinates types, please see the Geometry documentation
void IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const;
//##Documentation
//## @brief Convert (continuous or discrete) index coordinates of a \em point to world coordinates (in mm)
//## For further information about coordinates types, please see the Geometry documentation
void IndexToWorld(const mitk::Point3D &pt_units, mitk::Point3D &pt_mm) const;
//##Documentation
//## @brief Convert (discrete) index coordinates of a \em point to world coordinates (in mm)
//## For further information about coordinates types, please see the Geometry documentation
template <unsigned int VIndexDimension>
void IndexToWorld(const itk::Index<VIndexDimension> &index, mitk::Point3D &pt_mm) const
{
mitk::Point3D pt_units;
pt_units.Fill(0);
int i, dim = index.GetIndexDimension();
if (dim > 3)
{
dim = 3;
}
for (i = 0; i < dim; ++i)
{
pt_units[i] = index[i];
}
IndexToWorld(pt_units, pt_mm);
}
//##Documentation
//## @brief Convert (continuous or discrete) index coordinates of a \em vector
//## \a vec_units to world coordinates (in mm)
//## @deprecated First parameter (Point3D) is not used. If possible, please use void IndexToWorld(const
// mitk::Vector3D& vec_units, mitk::Vector3D& vec_mm) const.
//## For further information about coordinates types, please see the Geometry documentation
void IndexToWorld(const mitk::Point3D &atPt3d_units, const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const;
//##Documentation
//## @brief Convert world coordinates (in mm) of a \em vector
//## \a vec_mm to (continuous!) index coordinates.
//## @deprecated First parameter (Point3D) is not used. If possible, please use void WorldToIndex(const
// mitk::Vector3D& vec_mm, mitk::Vector3D& vec_units) const.
//## For further information about coordinates types, please see the Geometry documentation
void WorldToIndex(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const;
//##Documentation
//## @brief Deprecated for use with ITK version 3.10 or newer.
//## Convert ITK physical coordinates of a \em point (in mm,
//## but without a rotation) into MITK world coordinates (in mm)
//##
//## For more information, see WorldToItkPhysicalPoint.
template <class TCoordRep>
void ItkPhysicalPointToWorld(const itk::Point<TCoordRep, 3> &itkPhysicalPoint, mitk::Point3D &pt_mm) const
{
mitk::vtk2itk(itkPhysicalPoint, pt_mm);
}
//##Documentation
//## @brief Deprecated for use with ITK version 3.10 or newer.
//## Convert world coordinates (in mm) of a \em point to
//## ITK physical coordinates (in mm, but without a possible rotation)
//##
//## This method is useful if you have want to access an mitk::Image
//## via an itk::Image. ITK v3.8 and older did not support rotated (tilted)
//## images, i.e., ITK images are always parallel to the coordinate axes.
//## When accessing a (possibly rotated) mitk::Image via an itk::Image
//## the rotational part of the transformation in the BaseGeometry is
//## simply discarded; in other word: only the origin and spacing is
//## used by ITK, not the complete matrix available in MITK.
//## With WorldToItkPhysicalPoint you can convert an MITK world
//## coordinate (including the rotation) into a coordinate that
//## can be used with the ITK image as a ITK physical coordinate
//## (excluding the rotation).
template <class TCoordRep>
void WorldToItkPhysicalPoint(const mitk::Point3D &pt_mm, itk::Point<TCoordRep, 3> &itkPhysicalPoint) const
{
mitk::vtk2itk(pt_mm, itkPhysicalPoint);
}
// ********************************** BoundingBox **********************************
/** Get the bounding box */
itkGetConstObjectMacro(BoundingBox, BoundingBoxType);
// a bit of a misuse, but we want only doxygen to see the following:
#ifdef DOXYGEN_SKIP
//##Documentation
//## @brief Get bounding box (in index/unit coordinates)
itkGetConstObjectMacro(BoundingBox, BoundingBoxType);
//##Documentation
//## @brief Get bounding box (in index/unit coordinates) as a BoundsArrayType
const BoundsArrayType GetBounds() const;
#endif
const BoundsArrayType GetBounds() const;
//##Documentation
//## \brief Set the bounding box (in index/unit coordinates)
//##
//## Only possible via the BoundsArray to make clear that a
//## copy of the bounding-box is stored, not a reference to it.
void SetBounds(const BoundsArrayType &bounds);
//##Documentation
//## @brief Set the bounding box (in index/unit coordinates) via a float array
void SetFloatBounds(const float bounds[6]);
//##Documentation
//## @brief Set the bounding box (in index/unit coordinates) via a double array
void SetFloatBounds(const double bounds[6]);
//##Documentation
//## @brief Get a VnlVector along bounding-box in the specified
//## @a direction, length is spacing
//##
//## \sa GetAxisVector
VnlVector GetMatrixColumn(unsigned int direction) const;
//##Documentation
//## @brief Calculates a bounding-box around the geometry relative
//## to a coordinate system defined by a transform
//##
mitk::BoundingBox::Pointer CalculateBoundingBoxRelativeToTransform(const mitk::AffineTransform3D *transform) const;
//##Documentation
//## @brief Set the time bounds (in ms)
// void SetTimeBounds(const TimeBounds& timebounds);
// ********************************** Geometry **********************************
#ifdef DOXYGEN_SKIP
//##Documentation
//## @brief Get the extent of the bounding box (in index/unit coordinates)
//##
//## To access the extent in mm use GetExtentInMM
ScalarType GetExtent(unsigned int direction) const;
#endif
/** Get the extent of the bounding box */
ScalarType GetExtent(unsigned int direction) const;
//##Documentation
//## @brief Get the extent of the bounding-box in the specified @a direction in mm
//##
//## Equals length of GetAxisVector(direction).
ScalarType GetExtentInMM(int direction) const;
//##Documentation
//## @brief Get vector along bounding-box in the specified @a direction in mm
//##
//## The length of the vector is the size of the bounding-box in the
//## specified @a direction in mm
//## \sa GetMatrixColumn
Vector3D GetAxisVector(unsigned int direction) const;
//##Documentation
//## @brief Checks, if the given geometry can be converted to 2D without information loss
//## e.g. when a 2D image is saved, the matrix is usually cropped to 2x2, and when you load it back to MITK
//## it will be filled with standard values. This function checks, if information would be lost during this
//## procedure
virtual bool Is2DConvertable();
//##Documentation
//## @brief Get the center of the bounding-box in mm
//##
Point3D GetCenter() const;
//##Documentation
//## @brief Get the squared length of the diagonal of the bounding-box in mm
//##
double GetDiagonalLength2() const;
//##Documentation
//## @brief Get the length of the diagonal of the bounding-box in mm
//##
double GetDiagonalLength() const;
//##Documentation
//## @brief Get the position of the corner number \a id (in world coordinates)
//##
//## See SetImageGeometry for how a corner is defined on images.
Point3D GetCornerPoint(int id) const;
//##Documentation
//## @brief Get the position of a corner (in world coordinates)
//##
//## See SetImageGeometry for how a corner is defined on images.
Point3D GetCornerPoint(bool xFront = true, bool yFront = true, bool zFront = true) const;
//##Documentation
//## @brief Set the extent of the bounding-box in the specified @a direction in mm
//##
//## @note This changes the matrix in the transform, @a not the bounds, which are given in units!
void SetExtentInMM(int direction, ScalarType extentInMM);
//##Documentation
//## @brief Test whether the point \a p (world coordinates in mm) is
//## inside the bounding box
bool IsInside(const mitk::Point3D &p) const;
//##Documentation
//## @brief Test whether the point \a p ((continous!)index coordinates in units) is
//## inside the bounding box
bool IsIndexInside(const mitk::Point3D &index) const;
//##Documentation
//## @brief Convenience method for working with ITK indices
template <unsigned int VIndexDimension>
bool IsIndexInside(const itk::Index<VIndexDimension> &index) const
{
int i, dim = index.GetIndexDimension();
Point3D pt_index;
pt_index.Fill(0);
for (i = 0; i < dim; ++i)
{
pt_index[i] = index[i];
}
return IsIndexInside(pt_index);
}
// ********************************* Image Geometry ********************************
//##Documentation
//## @brief When switching from an Image Geometry to a normal Geometry (and the other way around), you have to
//change
// the origin as well (See Geometry Documentation)! This function will change the "isImageGeometry" bool flag and
// changes the origin respectively.
virtual void ChangeImageGeometryConsideringOriginOffset(const bool isAnImageGeometry);
//##Documentation
//## @brief Is this an ImageGeometry?
//##
//## For more information, see SetImageGeometry
itkGetConstMacro(ImageGeometry, bool)
//##Documentation
//## @brief Define that this BaseGeometry is refering to an Image
//##
//## A geometry referring to an Image needs a slightly different
//## definition of the position of the corners (see GetCornerPoint).
//## The position of a voxel is defined by the position of its center.
//## If we would use the origin (position of the (center of) the first
//## voxel) as a corner and display this point, it would seem to be
//## \em not at the corner but a bit within the image. Even worse for
//## the opposite corner of the image: here the corner would appear
//## outside the image (by half of the voxel diameter). Thus, we have
//## to correct for this and to be able to do that, we need to know
//## that the BaseGeometry is referring to an Image.
itkSetMacro(ImageGeometry, bool);
itkBooleanMacro(ImageGeometry);
const GeometryTransformHolder *GetGeometryTransformHolder() const;
protected:
// ********************************** Constructor **********************************
BaseGeometry();
BaseGeometry(const BaseGeometry &other);
~BaseGeometry() override;
itk::LightObject::Pointer InternalClone() const override = 0;
void PrintSelf(std::ostream &os, itk::Indent indent) const override;
static const std::string GetTransformAsString(TransformType *transformType);
itkGetConstMacro(NDimensions, unsigned int);
bool IsBoundingBoxNull() const;
bool IsIndexToWorldTransformNull() const;
void SetVtkMatrixDeepCopy(vtkTransform *vtktransform);
void _SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing = false);
//##Documentation
//## @brief PreSetSpacing
//##
//## These virtual function allows a different beahiour in subclasses.
//## Do implement them in every subclass of BaseGeometry. If not needed, use
//## {Superclass::PreSetSpacing();};
virtual void PreSetSpacing(const mitk::Vector3D & /*aSpacing*/){};
//##Documentation
//## @brief CheckBounds
//##
//## This function is called in SetBounds. Assertions can be implemented in this function (see PlaneGeometry.cpp).
//## If you implement this function in a subclass, make sure, that all classes were your class inherits from
//## have an implementation of CheckBounds
//## (e.g. inheritance BaseGeometry <- A <- B. Implementation of CheckBounds in class B needs implementation in A as
// well!)
virtual void CheckBounds(const BoundsArrayType & /*bounds*/){};
//##Documentation
//## @brief CheckIndexToWorldTransform
//##
//## This function is called in SetIndexToWorldTransform. Assertions can be implemented in this function (see
// PlaneGeometry.cpp).
//## In Subclasses of BaseGeometry, implement own conditions or call Superclass::CheckBounds(bounds);.
virtual void CheckIndexToWorldTransform(mitk::AffineTransform3D * /*transform*/){};
private:
GeometryTransformHolder *m_GeometryTransform;
void InitializeGeometryTransformHolder(const BaseGeometry *otherGeometry);
//##Documentation
//## @brief Bounding Box, which is axes-parallel in intrinsic coordinates
//## (often integer indices of pixels)
BoundingBoxPointer m_BoundingBox;
unsigned int m_FrameOfReferenceID;
// mitk::TimeBounds m_TimeBounds;
static const unsigned int m_NDimensions = 3;
mutable TransformType::Pointer m_InvertedTransform;
mutable unsigned long m_IndexToWorldTransformLastModified;
bool m_ImageGeometry;
//##Documentation
//## @brief ModifiedLockFlag is used to prohibit the call of Modified()
//##
//## For the use of this Flag, see class ModifiedLock. This flag should only be set
//## by the ModifiedLock class!
bool m_ModifiedLockFlag;
//##Documentation
//## @brief ModifiedcalledFlag is used to collect calls of Modified().
//##
//## For the use of this Flag, see class ModifiedLock. This flag should only be set
//## by the Modified() function!
mutable bool m_ModifiedCalledFlag;
};
// ********************************** Equal Functions **********************************
//
// Static compare functions mainly for testing
//
/**
* @brief Equal A function comparing two geometries for beeing identical.
*
* @ingroup MITKTestingAPI
*
* The function compares the spacing, origin, axisvectors, extents, the matrix of the
* IndexToWorldTransform (elementwise), the bounding (elementwise) and the ImageGeometry flag.
*
* The parameter eps is a tolarence value for all methods which are internally used for comparion.
* If you want to use different tolerance values for different parts of the geometry, feel free to use
* the other comparison methods and write your own implementation of Equal.
* @param rightHandSide Compare this against leftHandSide.
* @param leftHandSide Compare this against rightHandSide.
* @param coordinateEps Tolerance for comparison of all spatial aspects (spacing, origin and grid alignment).
* You can use mitk::eps in most cases.
* @param directionEps Tolerance for comparison of all directional aspects (axis). 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 comparison are true. False in any other case.
*/
MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry& leftHandSide,
const mitk::BaseGeometry& rightHandSide,
ScalarType coordinateEps,
ScalarType directionEps,
bool verbose = false);
/**
* @brief Equal A function comparing two geometries for beeing identical.
*
* @ingroup MITKTestingAPI
*
* This is an overloaded version that uses a single tolerance for spatial and directional aspects. For more details,
* see the other overloaded version.
*
* @param rightHandSide Compare this against leftHandSide.
* @param leftHandSide Compare this against rightHandSide.
* @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 comparison are true. False in any other case.
*/
MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry &leftHandSide,
const mitk::BaseGeometry &rightHandSide,
ScalarType eps = mitk::eps,
bool verbose = false);
/**
* @brief Equal A function comparing two transforms (TransformType) for beeing identical.
*
* @ingroup MITKTestingAPI
*
* The function compares the IndexToWorldTransform (elementwise).
*
* The parameter eps is a tolarence value for all methods which are internally used for comparion.
* @param rightHandSide Compare this against leftHandSide.
* @param leftHandSide Compare this against rightHandSide.
* @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 comparison are true. False in any other case.
*/
MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry::TransformType &leftHandSide,
const mitk::BaseGeometry::TransformType &rightHandSide,
ScalarType eps,
bool verbose);
/**
* @brief Equal A function comparing two bounding boxes (BoundingBoxType) for beeing identical.
*
* @ingroup MITKTestingAPI
*
* The function compares the bounds (elementwise).
*
* The parameter eps is a tolarence value for all methods which are internally used for comparion.
* @param rightHandSide Compare this against leftHandSide.
* @param leftHandSide Compare this against rightHandSide.
* @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 comparison are true. False in any other case.
*/
MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry::BoundingBoxType &leftHandSide,
const mitk::BaseGeometry::BoundingBoxType &rightHandSide,
ScalarType eps,
bool verbose);
/**
* @brief A function checks if a test geometry is a sub geometry of
* a given reference geometry.
*
* Sub geometry means that both geometries have the same voxel grid (same spacing, same axes,
* orgin is on voxel grid), but the bounding box of the checked geometry is contained or equal
* to the bounding box of the reference geometry.\n
* By this definition equal geometries are always sub geometries of each other.
*
* The function checks the spacing, origin, axis vectors, extents, the matrix of the
* IndexToWorldTransform (elementwise), the bounding (elementwise) and the ImageGeometry flag.
*
* The parameter eps is a tolerance value for all methods which are internally used for comparison.
* @param testGeo Geometry that should be checked if it is a sub geometry of referenceGeo.
* @param referenceGeo Geometry that should contain testedGeometry as sub geometry.
* @param coordinateEps Tolerance for comparison of all spatial aspects (spacing, origin and grid alignment).
* You can use mitk::eps in most cases.
* @param directionEps Tolerance for comparison of all directional aspects (axis). 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 comparisons are true. False otherwise.
*/
MITKCORE_EXPORT bool IsSubGeometry(const mitk::BaseGeometry& testGeo,
const mitk::BaseGeometry& referenceGeo,
ScalarType coordinateEps,
ScalarType directionEps,
bool verbose = false);
/**
* @brief A function checks if a test geometry is a sub geometry of
* a given reference geometry.
*
* This is a overloaded version that uses a single tolerance for spatial and directional aspects. For more details,
* see the other overloaded version.
*
* @param testGeo Geometry that should be checked if it is a sub geometry of referenceGeo.
* @param referenceGeo Geometry that should contain testedGeometry as sub geometry.
* @param eps Tolarence for comparison (both spatial and directional). 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 comparison are true. False otherwise.
*/
MITKCORE_EXPORT bool IsSubGeometry(const mitk::BaseGeometry& testGeo,
const mitk::BaseGeometry& referenceGeo,
ScalarType eps = mitk::eps,
bool verbose = false);
} // namespace mitk
#endif /* BaseGeometry_H_HEADER_INCLUDED */
diff --git a/Modules/Core/include/mitkBaseRenderer.h b/Modules/Core/include/mitkBaseRenderer.h
index 909de3a27c..8d2a45ef67 100644
--- a/Modules/Core/include/mitkBaseRenderer.h
+++ b/Modules/Core/include/mitkBaseRenderer.h
@@ -1,523 +1,523 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef BASERENDERER_H_HEADER_INCLUDED_C1CCA0F4
#define BASERENDERER_H_HEADER_INCLUDED_C1CCA0F4
#include "mitkCameraRotationController.h"
#include "mitkDataStorage.h"
#include "mitkPlaneGeometry.h"
#include "mitkPlaneGeometryData.h"
#include "mitkSliceNavigationController.h"
#include "mitkTimeGeometry.h"
#include "mitkBindDispatcherInteractor.h"
#include "mitkDispatcher.h"
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <map>
#include <set>
namespace mitk
{
class NavigationController;
class SliceNavigationController;
class CameraRotationController;
class CameraController;
class DataStorage;
class Mapper;
class BaseLocalStorageHandler;
class KeyEvent;
+#pragma GCC visibility push(default)
+ itkEventMacroDeclaration(RendererResetEvent, itk::AnyEvent);
+#pragma GCC visibility pop
+
//##Documentation
//## @brief Organizes the rendering process
//##
//## Organizes the rendering process. A Renderer contains a reference to a
//## DataStorage and asks the mappers of the data objects to render
//## the data into the renderwindow it is associated to.
//##
//## \#Render() checks if rendering is currently allowed by calling
//## RenderWindow::PrepareRendering(). Initialization of a rendering context
//## can also be performed in this method.
//##
//## The actual rendering code has been moved to \#Repaint()
//## Both \#Repaint() and \#Update() are declared protected now.
//##
//## Note: Separation of the Repaint and Update processes (rendering vs
//## creating a vtk prop tree) still needs to be worked on. The whole
//## rendering process also should be reworked to use VTK based classes for
//## both 2D and 3D rendering.
//## @ingroup Renderer
class MITKCORE_EXPORT BaseRenderer : public itk::Object
{
public:
typedef std::map<vtkRenderWindow *, BaseRenderer *> BaseRendererMapType;
static BaseRendererMapType baseRendererMap;
static BaseRenderer *GetInstance(vtkRenderWindow *renWin);
static void AddInstance(vtkRenderWindow *renWin, BaseRenderer *baseRenderer);
static void RemoveInstance(vtkRenderWindow *renWin);
static BaseRenderer *GetByName(const std::string &name);
static vtkRenderWindow *GetRenderWindowByName(const std::string &name);
-#pragma GCC visibility push(default)
- itkEventMacro(RendererResetEvent, itk::AnyEvent);
-#pragma GCC visibility pop
-
/** Standard class typedefs. */
mitkClassMacroItkParent(BaseRenderer, itk::Object);
BaseRenderer(const char *name = nullptr, vtkRenderWindow *renWin = nullptr);
//##Documentation
//## @brief MapperSlotId defines which kind of mapper (e.g. 2D or 3D) should be used.
typedef int MapperSlotId;
enum StandardMapperSlot
{
Standard2D = 1,
Standard3D = 2
};
//##Documentation
//## @brief Possible view directions for render windows.
enum class ViewDirection
{
AXIAL = 0,
SAGITTAL,
CORONAL,
THREE_D
};
virtual void SetDataStorage(DataStorage *storage); ///< set the datastorage that will be used for rendering
//##Documentation
//## return the DataStorage that is used for rendering
virtual DataStorage::Pointer GetDataStorage() const { return m_DataStorage.GetPointer(); }
//##Documentation
//## @brief Access the RenderWindow into which this renderer renders.
vtkRenderWindow *GetRenderWindow() const { return m_RenderWindow; }
vtkRenderer *GetVtkRenderer() const { return m_VtkRenderer; }
//##Documentation
//## @brief Returns the Dispatcher which handles Events for this BaseRenderer
Dispatcher::Pointer GetDispatcher() const;
//##Documentation
//## @brief Default mapper id to use.
static const MapperSlotId defaultMapper;
//##Documentation
//## @brief Do the rendering and flush the result.
virtual void Paint();
//##Documentation
//## @brief Initialize the RenderWindow. Should only be called from RenderWindow.
virtual void Initialize();
//##Documentation
//## @brief Called to inform the renderer that the RenderWindow has been resized.
virtual void Resize(int w, int h);
//##Documentation
//## @brief Initialize the renderer with a RenderWindow (@a renderwindow).
virtual void InitRenderer(vtkRenderWindow *renderwindow);
//##Documentation
//## @brief Set the initial size. Called by RenderWindow after it has become
//## visible for the first time.
virtual void InitSize(int w, int h);
//##Documentation
//## @brief Draws a point on the widget.
//## Should be used during conferences to show the position of the remote mouse
virtual void DrawOverlayMouse(Point2D &p2d);
//##Documentation
//## @brief Set/Get the WorldGeometry (m_WorldGeometry) for 3D and 2D rendering, that describing the
//## (maximal) area to be rendered.
//##
//## Depending of the type of the passed BaseGeometry more or less information can be extracted:
//## \li if it is a PlaneGeometry (which is a sub-class of BaseGeometry), m_CurrentWorldPlaneGeometry is
//## also set to point to it. m_WorldTimeGeometry is set to nullptr.
//## \li if it is a TimeGeometry, m_WorldTimeGeometry is also set to point to it.
//## If m_WorldTimeGeometry contains instances of SlicedGeometry3D, m_CurrentWorldPlaneGeometry is set to
//## one of geometries stored in the SlicedGeometry3D according to the value of m_Slice; otherwise
//## a PlaneGeometry describing the top of the bounding-box of the BaseGeometry is set as the
//## m_CurrentWorldPlaneGeometry.
//## \li otherwise a PlaneGeometry describing the top of the bounding-box of the BaseGeometry
//## is set as the m_CurrentWorldPlaneGeometry. m_WorldTimeGeometry is set to nullptr.
//## @todo add calculation of PlaneGeometry describing the top of the bounding-box of the BaseGeometry
//## when the passed BaseGeometry is not sliced.
//## \sa m_WorldGeometry
//## \sa m_WorldTimeGeometry
//## \sa m_CurrentWorldPlaneGeometry
virtual void SetWorldGeometry3D(const BaseGeometry *geometry);
virtual void SetWorldTimeGeometry(const mitk::TimeGeometry *geometry);
itkGetConstObjectMacro(WorldTimeGeometry, TimeGeometry);
//##Documentation
//## @brief Get the current 3D-worldgeometry (m_CurrentWorldGeometry) used for 3D-rendering
itkGetConstObjectMacro(CurrentWorldGeometry, BaseGeometry);
//##Documentation
//## @brief Get the current 2D-worldgeometry (m_CurrentWorldPlaneGeometry) used for 2D-rendering
itkGetConstObjectMacro(CurrentWorldPlaneGeometry, PlaneGeometry)
/**
* \deprecatedSince{2014_10} Please use GetCurrentWorldPlaneGeometry
*/
DEPRECATED(const PlaneGeometry *GetCurrentWorldGeometry2D())
{
return GetCurrentWorldPlaneGeometry();
};
//##Documentation
//## Calculates the bounds of the DataStorage (if it contains any valid data),
//## creates a geometry from these bounds and sets it as world geometry of the renderer.
//##
//## Call this method to re-initialize the renderer to the current DataStorage
//## (e.g. after loading an additional dataset), to ensure that the view is
//## aligned correctly.
//## \warning This is not implemented yet.
virtual bool SetWorldGeometryToDataStorageBounds() { return false; }
//##Documentation
//## @brief Set/Get m_Slice which defines together with m_TimeStep the 2D geometry
//## stored in m_WorldTimeGeometry used as m_CurrentWorldPlaneGeometry
//##
//## \sa m_Slice
virtual void SetSlice(unsigned int slice);
itkGetConstMacro(Slice, unsigned int);
//##Documentation
//## @brief Set/Get m_TimeStep which defines together with m_Slice the 2D geometry
//## stored in m_WorldTimeGeometry used as m_CurrentWorldPlaneGeometry
//##
//## \sa m_TimeStep
virtual void SetTimeStep(unsigned int timeStep);
itkGetConstMacro(TimeStep, unsigned int);
//##Documentation
//## @brief Get the time-step of a BaseData object which
//## exists at the time of the currently displayed content
//##
//## Returns -1 or mitk::BaseData::m_TimeSteps if there
//## is no data at the current time.
//## \sa GetTimeStep, m_TimeStep
TimeStepType GetTimeStep(const BaseData *data) const;
//##Documentation
//## @brief Get the time in ms of the currently displayed content
//##
//## \sa GetTimeStep, m_TimeStep
ScalarType GetTime() const;
//##Documentation
//## @brief SetWorldGeometry is called according to the geometrySliceEvent,
//## which is supposed to be a SliceNavigationController::GeometrySendEvent
virtual void SetGeometry(const itk::EventObject &geometrySliceEvent);
//##Documentation
//## @brief UpdateWorldGeometry is called to re-read the 2D geometry from the
//## slice navigation controller
virtual void UpdateGeometry(const itk::EventObject &geometrySliceEvent);
//##Documentation
//## @brief SetSlice is called according to the geometrySliceEvent,
//## which is supposed to be a SliceNavigationController::GeometrySliceEvent
virtual void SetGeometrySlice(const itk::EventObject &geometrySliceEvent);
//##Documentation
//## @brief SetTimeStep is called according to the geometrySliceEvent,
//## which is supposed to be a SliceNavigationController::GeometryTimeEvent
virtual void SetGeometryTime(const itk::EventObject &geometryTimeEvent);
//##Documentation
//## @brief Get a DataNode pointing to a data object containing the current 2D-worldgeometry
// m_CurrentWorldPlaneGeometry (for 2D rendering)
itkGetObjectMacro(CurrentWorldPlaneGeometryNode, DataNode)
/**
* \deprecatedSince{2014_10} Please use GetCurrentWorldPlaneGeometryNode
*/
DEPRECATED(DataNode *GetCurrentWorldGeometry2DNode())
{
return GetCurrentWorldPlaneGeometryNode();
};
//##Documentation
//## @brief Sets timestamp of CurrentWorldPlaneGeometry and forces so reslicing in that renderwindow
void SendUpdateSlice();
//##Documentation
//## @brief Get timestamp of last call of SetCurrentWorldPlaneGeometry
unsigned long GetCurrentWorldPlaneGeometryUpdateTime() { return m_CurrentWorldPlaneGeometryUpdateTime; }
/**
* \deprecatedSince{2014_10} Please use GetCurrentWorldPlaneGeometryUpdateTime
*/
DEPRECATED(unsigned long GetCurrentWorldGeometry2DUpdateTime())
{
return GetCurrentWorldPlaneGeometryUpdateTime();
};
//##Documentation
//## @brief Get timestamp of last change of current TimeStep
unsigned long GetTimeStepUpdateTime() { return m_TimeStepUpdateTime; }
//##Documentation
//## @brief Perform a picking: find the x,y,z world coordinate of a
//## display x,y coordinate.
//## @warning Has to be overwritten in subclasses for the 3D-case.
//##
//## Implemented here only for 2D-rendering
virtual void PickWorldPoint(const Point2D &diplayPosition, Point3D &worldPosition) const = 0;
/** \brief Determines the object (mitk::DataNode) closest to the current
* position by means of picking
*
* \warning Implementation currently empty for 2D rendering; intended to be
* implemented for 3D renderers */
virtual DataNode *PickObject(const Point2D & /*displayPosition*/, Point3D & /*worldPosition*/) const
{
return nullptr;
}
//##Documentation
//## @brief Get the MapperSlotId to use.
itkGetMacro(MapperID, MapperSlotId);
itkGetConstMacro(MapperID, MapperSlotId);
//##Documentation
//## @brief Set the MapperSlotId to use.
virtual void SetMapperID(MapperSlotId id);
virtual int *GetSize() const;
virtual int *GetViewportSize() const;
void SetSliceNavigationController(SliceNavigationController *SlicenavigationController);
itkGetObjectMacro(CameraController, CameraController);
itkGetObjectMacro(SliceNavigationController, SliceNavigationController);
itkGetObjectMacro(CameraRotationController, CameraRotationController);
itkGetMacro(EmptyWorldGeometry, bool);
//##Documentation
//## @brief Tells if the displayed region is shifted and rescaled if the render window is resized.
itkGetMacro(KeepDisplayedRegion, bool)
//##Documentation
//## @brief Tells if the displayed region should be shifted and rescaled if the render window is resized.
itkSetMacro(KeepDisplayedRegion, bool);
//##Documentation
//## @brief get the name of the Renderer
//## @note
const char *GetName() const
{
return m_Name.c_str();
}
//##Documentation
//## @brief get the x_size of the RendererWindow
//## @note
int GetSizeX() const { return GetSize()[0]; }
//##Documentation
//## @brief get the y_size of the RendererWindow
//## @note
int GetSizeY() const { return GetSize()[1]; }
const double *GetBounds() const;
void RequestUpdate();
void ForceImmediateUpdate();
/** Returns number of mappers which are visible and have level-of-detail
* rendering enabled */
unsigned int GetNumberOfVisibleLODEnabledMappers() const;
//##Documentation
//## @brief This method converts a display point to the 3D world index
//## using the geometry of the renderWindow.
void DisplayToWorld(const Point2D &displayPoint, Point3D &worldIndex) const;
//##Documentation
//## @brief This method converts a display point to the 2D world index, mapped onto the display plane
//## using the geometry of the renderWindow.
void DisplayToPlane(const Point2D &displayPoint, Point2D &planePointInMM) const;
//##Documentation
//## @brief This method converts a 3D world index to the display point
//## using the geometry of the renderWindow.
void WorldToDisplay(const Point3D &worldIndex, Point2D &displayPoint) const;
//##Documentation
//## @brief This method converts a 3D world index to the point on the viewport
//## using the geometry of the renderWindow.
void WorldToView(const Point3D &worldIndex, Point2D &viewPoint) const;
//##Documentation
//## @brief This method converts a 2D plane coordinate to the display point
//## using the geometry of the renderWindow.
void PlaneToDisplay(const Point2D &planePointInMM, Point2D &displayPoint) const;
//##Documentation
//## @brief This method converts a 2D plane coordinate to the point on the viewport
//## using the geometry of the renderWindow.
void PlaneToView(const Point2D &planePointInMM, Point2D &viewPoint) const;
double GetScaleFactorMMPerDisplayUnit() const;
Point2D GetDisplaySizeInMM() const;
Point2D GetViewportSizeInMM() const;
Point2D GetOriginInMM() const;
itkGetConstMacro(ConstrainZoomingAndPanning, bool) virtual void SetConstrainZoomingAndPanning(bool constrain);
/**
* \brief Provides (1) world coordinates for a given mouse position and (2)
* translates mousePosition to Display coordinates
* \deprecated Map2DRendererPositionTo3DWorldPosition is deprecated. Please use DisplayToWorld instead.
*/
DEPRECATED(virtual Point3D Map2DRendererPositionTo3DWorldPosition(const Point2D &mousePosition) const);
protected:
~BaseRenderer() override;
//##Documentation
//## @brief Call update of all mappers. To be implemented in subclasses.
virtual void Update() = 0;
vtkRenderWindow *m_RenderWindow;
vtkRenderer *m_VtkRenderer;
//##Documentation
//## @brief MapperSlotId to use. Defines which kind of mapper (e.g., 2D or 3D) shoud be used.
MapperSlotId m_MapperID;
//##Documentation
//## @brief The DataStorage that is used for rendering.
DataStorage::Pointer m_DataStorage;
//##Documentation
//## @brief Timestamp of last call of Update().
unsigned long m_LastUpdateTime;
//##Documentation
//## @brief CameraController for 3D rendering
//## @note preliminary.
itk::SmartPointer<CameraController> m_CameraController;
SliceNavigationController::Pointer m_SliceNavigationController;
CameraRotationController::Pointer m_CameraRotationController;
//##Documentation
//## @brief Sets m_CurrentWorldPlaneGeometry
virtual void SetCurrentWorldPlaneGeometry(const PlaneGeometry *geometry2d);
/**
* \deprecatedSince{2014_10} Please use SetCurrentWorldPlaneGeometry
*/
DEPRECATED(void SetCurrentWorldGeometry2D(PlaneGeometry *geometry2d)) { SetCurrentWorldPlaneGeometry(geometry2d); };
//##Documentation
//## @brief Sets m_CurrentWorldGeometry
virtual void SetCurrentWorldGeometry(const BaseGeometry *geometry);
private:
//##Documentation
//## m_WorldTimeGeometry is set by SetWorldGeometry if the passed BaseGeometry is a
//## TimeGeometry (or a sub-class of it). If it contains instances of SlicedGeometry3D,
//## m_Slice and m_TimeStep (set via SetSlice and SetTimeStep, respectively) define
//## which 2D geometry stored in m_WorldTimeGeometry (if available)
//## is used as m_CurrentWorldPlaneGeometry.
//## \sa m_CurrentWorldPlaneGeometry
TimeGeometry::ConstPointer m_WorldTimeGeometry;
//##Documentation
//## Pointer to the current 3D-worldgeometry.
BaseGeometry::ConstPointer m_CurrentWorldGeometry;
//##Documentation
//## Pointer to the current 2D-worldgeometry. The 2D-worldgeometry
//## describes the maximal area (2D manifold) to be rendered in case we
//## are doing 2D-rendering.
//## It is const, since we are not allowed to change it (it may be taken
//## directly from the geometry of an image-slice and thus it would be
//## very strange when suddenly the image-slice changes its geometry).
PlaneGeometry::Pointer m_CurrentWorldPlaneGeometry;
//##Documentation
//## Defines together with m_Slice which 2D geometry stored in m_WorldTimeGeometry
//## is used as m_CurrentWorldPlaneGeometry: m_WorldTimeGeometry->GetPlaneGeometry(m_Slice, m_TimeStep).
//## \sa m_WorldTimeGeometry
unsigned int m_Slice;
//##Documentation
//## Defines together with m_TimeStep which 2D geometry stored in m_WorldTimeGeometry
//## is used as m_CurrentWorldPlaneGeometry: m_WorldTimeGeometry->GetPlaneGeometry(m_Slice, m_TimeStep).
//## \sa m_WorldTimeGeometry
unsigned int m_TimeStep;
//##Documentation
//## @brief timestamp of last call of SetWorldGeometry
itk::TimeStamp m_CurrentWorldPlaneGeometryUpdateTime;
//##Documentation
//## @brief timestamp of last change of the current time step
itk::TimeStamp m_TimeStepUpdateTime;
//##Documentation
//## @brief Helper class which establishes connection between Interactors and Dispatcher via a common DataStorage.
BindDispatcherInteractor *m_BindDispatcherInteractor;
//##Documentation
//## @brief Tells if the displayed region should be shifted or rescaled if the render window is resized.
bool m_KeepDisplayedRegion;
protected:
void PrintSelf(std::ostream &os, itk::Indent indent) const override;
//##Documentation
//## Data object containing the m_CurrentWorldPlaneGeometry defined above.
PlaneGeometryData::Pointer m_CurrentWorldPlaneGeometryData;
//##Documentation
//## DataNode objects containing the m_CurrentWorldPlaneGeometryData defined above.
DataNode::Pointer m_CurrentWorldPlaneGeometryNode;
//##Documentation
//## @brief test only
unsigned long m_CurrentWorldPlaneGeometryTransformTime;
std::string m_Name;
double m_Bounds[6];
bool m_EmptyWorldGeometry;
typedef std::set<Mapper *> LODEnabledMappersType;
/** Number of mappers which are visible and have level-of-detail
* rendering enabled */
unsigned int m_NumberOfVisibleLODEnabledMappers;
// Local Storage Handling for mappers
protected:
std::list<mitk::BaseLocalStorageHandler *> m_RegisteredLocalStorageHandlers;
bool m_ConstrainZoomingAndPanning;
public:
void RemoveAllLocalStorages();
void RegisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh);
void UnregisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh);
};
} // namespace mitk
#endif /* BASERENDERER_H_HEADER_INCLUDED_C1CCA0F4 */
diff --git a/Modules/Core/include/mitkDataInteractor.h b/Modules/Core/include/mitkDataInteractor.h
index 66dcb101d3..b25e689d79 100644
--- a/Modules/Core/include/mitkDataInteractor.h
+++ b/Modules/Core/include/mitkDataInteractor.h
@@ -1,113 +1,113 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKDATAINTERACTOR_H_
#define MITKDATAINTERACTOR_H_
#include <MitkCoreExports.h>
#include <mitkCommon.h>
#include <mitkEventStateMachine.h>
#include <mitkWeakPointer.h>
#include <string>
namespace mitk
{
class DataNode;
- itkEventMacro(DataInteractorEvent, itk::AnyEvent);
+ itkEventMacroDeclaration(DataInteractorEvent, itk::AnyEvent);
- /** Triggered when interaction is started */
- itkEventMacro(StartInteraction, DataInteractorEvent);
+ /** Triggered when interaction is started */
+ itkEventMacroDeclaration(StartInteraction, DataInteractorEvent);
- /** Triggered when result is stored in mitk::DataNode */
- itkEventMacro(ResultReady, DataInteractorEvent);
+ /** Triggered when result is stored in mitk::DataNode */
+ itkEventMacroDeclaration(ResultReady, DataInteractorEvent);
- enum ProcessEventMode {
- REGULAR = 0,
- GRABINPUT = 1,
- PREFERINPUT = 2,
- CONNECTEDMOUSEACTION = 3
- };
+ enum ProcessEventMode {
+ REGULAR = 0,
+ GRABINPUT = 1,
+ PREFERINPUT = 2,
+ CONNECTEDMOUSEACTION = 3
+ };
/**
* \brief Base class from with interactors that handle DataNodes are to be derived.
*
* Base class from with interactors that handle DataNodes are to be derived.
* Provides an interface that is relevant for the interactor to work together with the dispatcher.
* To implement a new interactor overwrite the ConnectActionsAndFunctions to connect the actions.
*/
class MITKCORE_EXPORT DataInteractor : public EventStateMachine
{
public:
// Predefined internal events/signals
static const std::string IntDeactivateMe;
static const std::string IntLeaveWidget;
static const std::string IntEnterWidget;
mitkClassMacro(DataInteractor, EventStateMachine);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
DataNode *GetDataNode() const;
virtual void SetDataNode(DataNode *dataNode); // TODO: Remove virtual, use DataNodeChanged instead in subclasses
int GetLayer() const;
ProcessEventMode GetMode() const;
protected:
DataInteractor();
~DataInteractor() override;
/**
* @brief Overwrite this function to connect actions from StateMachine description with functions.
*
* Following example shows how to connect the 'addpoint' action from the StateMachine XML description using the
CONNECT_FUNCTION macro
* with the AddPoint() function in the TestInteractor.
* @code
* void mitk::TestInteractor::ConnectActionsAndFunctions()
{
CONNECT_FUNCTION("addpoint", AddPoint);
}
* @endcode
*/
void ConnectActionsAndFunctions() override;
/** \brief Is called when a DataNode is initially set or changed
* To be implemented by sub-classes for initialization code which require a DataNode.
* \note New DataInteractors usually are expected to have the focus, but this only works if they have the highest
* Layer,
* since empty DataNodes have a layer of -1, the DataNode must be filled here in order to get a layer assigned.
* \note Is also called when the DataNode is set to nullptr.
*/
virtual void DataNodeChanged();
/**
* @brief Sends StartInteraction event via the mitk::DataNode
*/
void virtual NotifyStart();
/**
* @brief NotifyResultReady Sends ResultReady event via the mitk::DataNode
*
* Use to get notfied when the mitk::DataNode is in a ready state for further processing.
*/
void virtual NotifyResultReady();
private:
WeakPointer<DataNode> m_DataNode;
};
}
#endif
diff --git a/Modules/Core/include/mitkDataNode.h b/Modules/Core/include/mitkDataNode.h
index 24f5890c5b..74dedd5ae5 100644
--- a/Modules/Core/include/mitkDataNode.h
+++ b/Modules/Core/include/mitkDataNode.h
@@ -1,597 +1,598 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef DATATREENODE_H_HEADER_INCLUDED_C1E14338
#define DATATREENODE_H_HEADER_INCLUDED_C1E14338
#include "mitkBaseData.h"
//#include "mitkMapper.h"
#include "mitkDataInteractor.h"
#include "mitkIdentifiable.h"
#include "mitkIPropertyOwner.h"
#include <fstream>
#include <iostream>
#include "mitkColorProperty.h"
#include "mitkPropertyList.h"
#include "mitkStringProperty.h"
//#include "mitkMapper.h"
#include "mitkGeometry3D.h"
#include "mitkLevelWindow.h"
#include <map>
#include <set>
class vtkLinearTransform;
namespace mitk
{
class BaseRenderer;
class Mapper;
+ /**
+ * \brief Definition of an itk::Event that is invoked when
+ * a DataInteractor is set on this DataNode.
+ */
+ itkEventMacroDeclaration(InteractorChangedEvent, itk::AnyEvent);
+
/**
* \brief Class for nodes of the DataTree
*
* Contains the data (instance of BaseData), a list of mappers, which can
* draw the data, a transform (vtkTransform) and a list of properties
* (PropertyList).
* \ingroup DataManagement
*
* \todo clean up all the GetProperty methods. There are too many different flavours... Can most probably be reduced
* to
* <tt>bool GetProperty<type>(type&)</tt>
*
* \warning Change in semantics of SetProperty() since Aug 25th 2006. Check your usage of this method if you do
* more with properties than just call <tt>SetProperty( "key", new SomeProperty("value") )</tt>.
*/
class MITKCORE_EXPORT DataNode : public itk::DataObject, public IPropertyOwner
{
public:
typedef mitk::Geometry3D::Pointer Geometry3DPointer;
typedef std::vector<itk::SmartPointer<Mapper>> MapperVector;
typedef std::map<std::string, mitk::PropertyList::Pointer> MapOfPropertyLists;
typedef std::vector<MapOfPropertyLists::key_type> PropertyListKeyNames;
typedef std::set<std::string> GroupTagList;
- /**
- * \brief Definition of an itk::Event that is invoked when
- * a DataInteractor is set on this DataNode.
- */
- itkEventMacro(InteractorChangedEvent, itk::AnyEvent)
mitkClassMacroItkParent(DataNode, itk::DataObject);
itkFactorylessNewMacro(Self);
// IPropertyProvider
BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) const override;
std::vector<std::string> GetPropertyKeys(const std::string &contextName = "", bool includeDefaultContext = false) const override;
std::vector<std::string> GetPropertyContextNames() const override;
// IPropertyOwner
BaseProperty * GetNonConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) override;
void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override;
void RemoveProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override;
mitk::Mapper *GetMapper(MapperSlotId id) const;
/**
* \brief Get the data object (instance of BaseData, e.g., an Image)
* managed by this DataNode
*/
BaseData *GetData() const;
/**
* \brief Get the transformation applied prior to displaying the data as
* a vtkTransform
* \deprecated use GetData()->GetGeometry()->GetVtkTransform() instead
*/
vtkLinearTransform *GetVtkTransform(int t = 0) const;
/**
* \brief Set the data object (instance of BaseData, e.g., an Image)
* managed by this DataNode
*
* Prior set properties are kept if previous data of the node already exists and has the same
* type as the new data to be set. Otherwise, the default properties are used.
* In case that previous data already exists, the property list of the data node is cleared
* before setting new default properties.
*
* \warning the actor-mode of the vtkInteractor does not work any more, if the transform of the
* data-tree-node is connected to the transform of the basedata via vtkTransform->SetInput.
*/
virtual void SetData(mitk::BaseData *baseData);
/**
* \brief Set the Interactor.
*/
virtual void SetDataInteractor(const DataInteractor::Pointer interactor);
virtual DataInteractor::Pointer GetDataInteractor() const;
mitk::DataNode &operator=(const DataNode &right);
mitk::DataNode &operator=(BaseData *right);
virtual void SetMapper(MapperSlotId id, mitk::Mapper *mapper);
void UpdateOutputInformation() override;
void SetRequestedRegionToLargestPossibleRegion() override;
bool RequestedRegionIsOutsideOfTheBufferedRegion() override;
bool VerifyRequestedRegion() override;
void SetRequestedRegion(const itk::DataObject *data) override;
void CopyInformation(const itk::DataObject *data) override;
/**
* \brief The "names" used for (renderer-specific) PropertyLists in GetPropertyList(string).
*
* All possible values for the "renderer" parameters of
* the diverse GetProperty/List() methods.
*/
PropertyListKeyNames GetPropertyListNames() const;
/**
* \brief Set the property (instance of BaseProperty) with key \a propertyKey in the PropertyList
* of the \a renderer (if nullptr, use BaseRenderer-independent PropertyList). This is set-by-value.
*
* \warning Change in semantics since Aug 25th 2006. Check your usage of this method if you do
* more with properties than just call <tt>SetProperty( "key", new SomeProperty("value") )</tt>.
*
* \sa GetProperty
* \sa m_PropertyList
* \sa m_MapOfPropertyLists
*/
void SetProperty(const char *propertyKey, BaseProperty *property, const mitk::BaseRenderer *renderer = nullptr);
/**
* \brief Replace the property (instance of BaseProperty) with key \a propertyKey in the PropertyList
* of the \a renderer (if nullptr, use BaseRenderer-independent PropertyList). This is set-by-reference.
*
* If \a renderer is \a nullptr the property is set in the BaseRenderer-independent
* PropertyList of this DataNode.
* \sa GetProperty
* \sa m_PropertyList
* \sa m_MapOfPropertyLists
*/
void ReplaceProperty(const char *propertyKey, BaseProperty *property, const mitk::BaseRenderer *renderer = nullptr);
/**
* \brief Add the property (instance of BaseProperty) if it does
* not exist (or always if\a overwrite is\a true)
* with key \a propertyKey in the PropertyList
* of the \a renderer (if nullptr, use BaseRenderer-independent
* PropertyList). This is set-by-value.
*
* For\a overwrite ==\a false the property is\em not changed
* if it already exists. For\a overwrite ==\a true the method
* is identical to SetProperty.
*
* \sa SetProperty
* \sa GetProperty
* \sa m_PropertyList
* \sa m_MapOfPropertyLists
*/
void AddProperty(const char *propertyKey,
BaseProperty *property,
const mitk::BaseRenderer *renderer = nullptr,
bool overwrite = false);
/**
* \brief Get the PropertyList of the \a renderer. If \a renderer is \a
* nullptr, the BaseRenderer-independent PropertyList of this DataNode
* is returned.
* \sa GetProperty
* \sa m_PropertyList
* \sa m_MapOfPropertyLists
*/
mitk::PropertyList *GetPropertyList(const mitk::BaseRenderer *renderer = nullptr) const;
mitk::PropertyList *GetPropertyList(const std::string &rendererName) const;
/**
* \brief Add values from another PropertyList.
*
* Overwrites values in m_PropertyList only when possible (i.e. when types are compatible).
* If you want to allow for object type changes (replacing a "visible":BoolProperty with "visible":IntProperty,
* set \c replace .
*
* \param replace true: if \param pList contains a property "visible" of type ColorProperty and our m_PropertyList
* also has a "visible" property of a different type (e.g. BoolProperty), change the type, i.e. replace the objects
* behind the pointer.
*
* \sa SetProperty
* \sa ReplaceProperty
* \sa m_PropertyList
*/
void ConcatenatePropertyList(PropertyList *pList, bool replace = false);
/**
* \brief Get the property (instance of BaseProperty) with key \a propertyKey from the PropertyList
* of the \a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList.
*
* If \a renderer is \a nullptr or the \a propertyKey cannot be found
* in the PropertyList specific to \a renderer or is disabled there, the BaseRenderer-independent
* PropertyList of this DataNode is queried.
*
* If \a fallBackOnDataProperties is true, the data property list is queried as a last resort.
*
* \sa GetPropertyList
* \sa m_PropertyList
* \sa m_MapOfPropertyLists
*/
mitk::BaseProperty *GetProperty(const char *propertyKey, const mitk::BaseRenderer *renderer = nullptr, bool fallBackOnDataProperties = true) const;
/**
* \brief Get the property of type T with key \a propertyKey from the PropertyList
* of the \a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList.
*
* If \a renderer is \a nullptr or the \a propertyKey cannot be found
* in the PropertyList specific to \a renderer or is disabled there, the BaseRenderer-independent
* PropertyList of this DataNode is queried.
* \sa GetPropertyList
* \sa m_PropertyList
* \sa m_MapOfPropertyLists
*/
template <typename T>
bool GetProperty(itk::SmartPointer<T> &property,
const char *propertyKey,
const mitk::BaseRenderer *renderer = nullptr) const
{
property = dynamic_cast<T *>(GetProperty(propertyKey, renderer));
return property.IsNotNull();
}
/**
* \brief Get the property of type T with key \a propertyKey from the PropertyList
* of the \a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList.
*
* If \a renderer is \a nullptr or the \a propertyKey cannot be found
* in the PropertyList specific to \a renderer or is disabled there, the BaseRenderer-independent
* PropertyList of this DataNode is queried.
* \sa GetPropertyList
* \sa m_PropertyList
* \sa m_MapOfPropertyLists
*/
template <typename T>
bool GetProperty(T *&property, const char *propertyKey, const mitk::BaseRenderer *renderer = nullptr) const
{
property = dynamic_cast<T *>(GetProperty(propertyKey, renderer));
return property != nullptr;
}
/**
* \brief Convenience access method for GenericProperty<T> properties
* (T being the type of the second parameter)
* \return \a true property was found
*/
template <typename T>
bool GetPropertyValue(const char *propertyKey, T &value, const mitk::BaseRenderer *renderer = nullptr) const
{
GenericProperty<T> *gp = dynamic_cast<GenericProperty<T> *>(GetProperty(propertyKey, renderer));
if (gp != nullptr)
{
value = gp->GetValue();
return true;
}
return false;
}
/// \brief Get a set of all group tags from this node's property list
GroupTagList GetGroupTags() const;
/**
* \brief Convenience access method for bool properties (instances of
* BoolProperty)
* \return \a true property was found
*/
bool GetBoolProperty(const char *propertyKey, bool &boolValue, const mitk::BaseRenderer *renderer = nullptr) const;
/**
* \brief Convenience access method for int properties (instances of
* IntProperty)
* \return \a true property was found
*/
bool GetIntProperty(const char *propertyKey, int &intValue, const mitk::BaseRenderer *renderer = nullptr) const;
/**
* \brief Convenience access method for float properties (instances of
* FloatProperty)
* \return \a true property was found
*/
bool GetFloatProperty(const char *propertyKey,
float &floatValue,
const mitk::BaseRenderer *renderer = nullptr) const;
/**
* \brief Convenience access method for double properties (instances of
* DoubleProperty)
*
* If there is no DoubleProperty for the given\c propertyKey argument, the method
* looks for a corresponding FloatProperty instance.
*
* \return \a true property was found
*/
bool GetDoubleProperty(const char *propertyKey,
double &doubleValue,
const mitk::BaseRenderer *renderer = nullptr) const;
/**
* \brief Convenience access method for string properties (instances of
* StringProperty)
* \return \a true property was found
*/
bool GetStringProperty(const char *propertyKey,
std::string &string,
const mitk::BaseRenderer *renderer = nullptr) const;
/**
* \brief Convenience access method for color properties (instances of
* ColorProperty)
* \return \a true property was found
*/
bool GetColor(float rgb[3], const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color") const;
/**
* \brief Convenience access method for level-window properties (instances of
* LevelWindowProperty)
* \return \a true property was found
*/
bool GetLevelWindow(mitk::LevelWindow &levelWindow,
const mitk::BaseRenderer *renderer = nullptr,
const char *propertyKey = "levelwindow") const;
/**
* \brief set the node as selected
*/
void SetSelected(bool selected, const mitk::BaseRenderer *renderer = nullptr);
/**
* \brief set the node as selected
* \return \a true node is selected
*/
bool IsSelected(const mitk::BaseRenderer *renderer = nullptr);
/**
* \brief Convenience access method for accessing the name of an object (instance of
* StringProperty with property-key "name")
* \return \a true property was found
*/
bool GetName(std::string &nodeName,
const mitk::BaseRenderer *renderer = nullptr,
const char *propertyKey = "name") const
{
return GetStringProperty(propertyKey, nodeName, renderer);
}
/**
* \brief Extra convenience access method for accessing the name of an object (instance of
* StringProperty with property-key "name").
*
* This method does not take the renderer specific
* propertylists into account, because the name of an object should never be renderer specific.
* \returns a std::string with the name of the object (content of "name" Property).
* If there is no "name" Property, an empty string will be returned.
*/
virtual std::string GetName() const
{
mitk::StringProperty *sp = dynamic_cast<mitk::StringProperty *>(this->GetProperty("name"));
if (sp == nullptr)
return "";
return sp->GetValue();
}
/** Value constant that is used indicate that node names are not set so far.*/
static std::string NO_NAME_VALUE()
{
return "No Name!";
}
/**
* \brief Extra convenience access method to set the name of an object.
*
* The name will be stored in the non-renderer-specific PropertyList in a StringProperty named "name".
*/
virtual void SetName(const char *name)
{
if (name == nullptr)
return;
this->SetProperty("name", StringProperty::New(name));
}
/**
* \brief Extra convenience access method to set the name of an object.
*
* The name will be stored in the non-renderer-specific PropertyList in a StringProperty named "name".
*/
virtual void SetName(const std::string name) { this->SetName(name.c_str()); }
/**
* \brief Convenience access method for visibility properties (instances
* of BoolProperty with property-key "visible")
* \return \a true property was found
* \sa IsVisible
*/
bool GetVisibility(bool &visible, const mitk::BaseRenderer *renderer, const char *propertyKey = "visible") const
{
return GetBoolProperty(propertyKey, visible, renderer);
}
/**
* \brief Convenience access method for opacity properties (instances of
* FloatProperty)
* \return \a true property was found
*/
bool GetOpacity(float &opacity, const mitk::BaseRenderer *renderer, const char *propertyKey = "opacity") const;
/**
* \brief Convenience access method for boolean properties (instances
* of BoolProperty). Return value is the value of the property. If the property is
* not found, the value of \a defaultIsOn is returned.
*
* Thus, the return value has a different meaning than in the
* GetBoolProperty method!
* \sa GetBoolProperty
*/
bool IsOn(const char *propertyKey, const mitk::BaseRenderer *renderer, bool defaultIsOn = true) const
{
if (propertyKey == nullptr)
return defaultIsOn;
GetBoolProperty(propertyKey, defaultIsOn, renderer);
return defaultIsOn;
}
/**
* \brief Convenience access method for visibility properties (instances
* of BoolProperty). Return value is the visibility. Default is
* visible==true, i.e., true is returned even if the property (\a
* propertyKey) is not found.
*
* Thus, the return value has a different meaning than in the
* GetVisibility method!
* \sa GetVisibility
* \sa IsOn
*/
bool IsVisible(const mitk::BaseRenderer *renderer,
const char *propertyKey = "visible",
bool defaultIsOn = true) const
{
return IsOn(propertyKey, renderer, defaultIsOn);
}
/**
* \brief Convenience method for setting color properties (instances of
* ColorProperty)
*/
void SetColor(const mitk::Color &color,
const mitk::BaseRenderer *renderer = nullptr,
const char *propertyKey = "color");
/**
* \brief Convenience method for setting color properties (instances of
* ColorProperty)
*/
void SetColor(float red,
float green,
float blue,
const mitk::BaseRenderer *renderer = nullptr,
const char *propertyKey = "color");
/**
* \brief Convenience method for setting color properties (instances of
* ColorProperty)
*/
void SetColor(const float rgb[3], const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color");
/**
* \brief Convenience method for setting visibility properties (instances
* of BoolProperty)
* \param visible If set to true, the data will be rendered. If false, the render will skip this data.
* \param renderer Specify a renderer if the visibility shall be specific to a renderer
* \param propertyKey Can be used to specify a user defined name of the visibility propery.
*/
void SetVisibility(bool visible, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "visible");
/**
* \brief Convenience method for setting opacity properties (instances of
* FloatProperty)
*/
void SetOpacity(float opacity, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "opacity");
/**
* \brief Convenience method for setting level-window properties
* (instances of LevelWindowProperty)
*/
void SetLevelWindow(mitk::LevelWindow levelWindow,
const mitk::BaseRenderer *renderer = nullptr,
const char *propertyKey = "levelwindow");
/**
* \brief Convenience method for setting int properties (instances of
* IntProperty)
*/
void SetIntProperty(const char *propertyKey, int intValue, const mitk::BaseRenderer *renderer = nullptr);
/**
* \brief Convenience method for setting boolean properties (instances of
* BoolProperty)
*/
void SetBoolProperty(const char *propertyKey, bool boolValue, const mitk::BaseRenderer *renderer = nullptr);
/**
* \brief Convenience method for setting float properties (instances of
* FloatProperty)
*/
void SetFloatProperty(const char *propertyKey, float floatValue, const mitk::BaseRenderer *renderer = nullptr);
/**
* \brief Convenience method for setting double properties (instances of
* DoubleProperty)
*/
void SetDoubleProperty(const char *propertyKey, double doubleValue, const mitk::BaseRenderer *renderer = nullptr);
/**
* \brief Convenience method for setting string properties (instances of
* StringProperty)
*/
void SetStringProperty(const char *propertyKey, const char *string, const mitk::BaseRenderer *renderer = nullptr);
/**
* \brief Get the timestamp of the last change of the contents of this node or
* the referenced BaseData.
*/
- unsigned long GetMTime() const override;
+ itk::ModifiedTimeType GetMTime() const override;
/**
* \brief Get the timestamp of the last change of the reference to the
* BaseData.
*/
unsigned long GetDataReferenceChangedTime() const { return m_DataReferenceChangedTime.GetMTime(); }
protected:
DataNode();
~DataNode() override;
/// Invoked when the property list was modified. Calls Modified() of the DataNode
virtual void PropertyListModified(const itk::Object *caller, const itk::EventObject &event);
/// \brief Mapper-slots
mutable MapperVector m_Mappers;
/**
* \brief The data object (instance of BaseData, e.g., an Image) managed
* by this DataNode
*/
BaseData::Pointer m_Data;
/**
* \brief BaseRenderer-independent PropertyList
*
* Properties herein can be overwritten specifically for each BaseRenderer
* by the BaseRenderer-specific properties defined in m_MapOfPropertyLists.
*/
PropertyList::Pointer m_PropertyList;
/// \brief Map associating each BaseRenderer with its own PropertyList
mutable MapOfPropertyLists m_MapOfPropertyLists;
DataInteractor::Pointer m_DataInteractor;
/// \brief Timestamp of the last change of m_Data
itk::TimeStamp m_DataReferenceChangedTime;
unsigned long m_PropertyListModifiedObserverTag;
};
MITKCORE_EXPORT std::istream &operator>>(std::istream &i, DataNode::Pointer &dtn);
MITKCORE_EXPORT std::ostream &operator<<(std::ostream &o, DataNode::Pointer &dtn);
} // namespace mitk
#endif /* DATATREENODE_H_HEADER_INCLUDED_C1E14338 */
diff --git a/Modules/Core/include/mitkDataStorage.h b/Modules/Core/include/mitkDataStorage.h
index a860959317..b884a7b12d 100644
--- a/Modules/Core/include/mitkDataStorage.h
+++ b/Modules/Core/include/mitkDataStorage.h
@@ -1,442 +1,442 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKDATASTORAGE_H
#define MITKDATASTORAGE_H
#include "itkObject.h"
-#include "itkSimpleFastMutexLock.h"
#include "itkVectorContainer.h"
#include "mitkDataNode.h"
#include "mitkGeometry3D.h"
#include "mitkMessage.h"
#include <MitkCoreExports.h>
#include <map>
+#include <mutex>
namespace mitk
{
class NodePredicateBase;
class DataNode;
class BaseRenderer;
//##Documentation
//## @brief Data management class that handles 'was created by' relations
//##
//## The DataStorage provides data storage and management functionality.
//## It handles a 'was created by' relation by associating each data object with a
//## set of source objects, that this object was created from.
//## Thus, nodes are stored in a noncyclical directed graph data structure.
//## If a new node is added to the DataStorage, AddNodeEvent is emitted.
//## If a node is removed, RemoveNodeEvent is emitted.
//##
//##
//## \ingroup DataStorage
class MITKCORE_EXPORT DataStorage : public itk::Object
{
public:
mitkClassMacroItkParent(DataStorage, itk::Object);
//##Documentation
//## @brief A Container of objects that is used as a result set of GetSubset() query operations (Set of
//SmartPointers
// to DataNodes).
typedef itk::VectorContainer<unsigned int, DataNode::Pointer> SetOfObjects;
//##Documentation
//## @brief Adds a DataNode containing a data object to its internal storage
//##
//## This Method adds a new data object to the DataStorage. The new object is
//## passed in the first parameter. The second parameter is a set
//## of source objects, that were used to create this object. The new object will have
//## a 'was created from' relation to its source objects.
//## the addition of a new object will fire the notification mechanism.
//## If the node parameter is nullptr or if the DataNode has already been added,
//## an exception will be thrown.
virtual void Add(DataNode *node, const DataStorage::SetOfObjects *parents = nullptr) = 0;
//##Documentation
//## @brief Convenience method to add a node that has one parent
//##
void Add(DataNode *node, DataNode *parent);
//##Documentation
//## @brief Removes node from the DataStorage
//##
virtual void Remove(const DataNode *node) = 0;
//##Documentation
//## @brief Checks if a node exists in the DataStorage
//##
virtual bool Exists(const DataNode *node) const = 0;
//##Documentation
//## @brief Removes a set of nodes from the DataStorage
//##
void Remove(const DataStorage::SetOfObjects *nodes);
//##Documentation
//## @brief returns a set of data objects that meet the given condition(s)
//##
//## GetSubset returns a set of objects with a specific data type that meet the condition(s)
//## specified in the condition parameter. Conditions can be
//## - data type of the data object
//## - is source object of specific object (e.g. all source objects of node x)
//## - has property with specific value (e.g. OrganType is Liver)
//## - negation of any condition
//## - conjunction of a set of conditions
//## - disjunction of a set of conditions
//## Conditions are implemented as predicates using the Composite Design Pattern
//## (see definition of NodePredicateBase for details).
//## The method returns a set of SmartPointers to the DataNodes that fulfill the
//## conditions. A set of all objects can be retrieved with the GetAll() method;
SetOfObjects::ConstPointer GetSubset(const NodePredicateBase *condition) const;
//##Documentation
//## @brief returns a set of source objects for a given node that meet the given condition(s).
//##
virtual SetOfObjects::ConstPointer GetSources(const DataNode *node,
const NodePredicateBase *condition = nullptr,
bool onlyDirectSources = true) const = 0;
//##Documentation
//## @brief returns a set of derived objects for a given node.
//##
//## GetDerivations() returns a set of objects that are derived from the DataNode node.
//## This means, that node was used to create the returned objects. If the parameter
//## onlyDirectDerivations is set to true (default value), only objects that directly have
//## node as one of their source objects will be returned. Otherwise, objects that are
//## derived from derivations of node are returned too.
//## The derived objects can be filtered with a predicate object as described in the GetSubset()
//## method by providing a predicate as the condition parameter.
virtual SetOfObjects::ConstPointer GetDerivations(const DataNode *node,
const NodePredicateBase *condition = nullptr,
bool onlyDirectDerivations = true) const = 0;
//##Documentation
//## @brief returns a set of all data objects that are stored in the data storage
//##
virtual SetOfObjects::ConstPointer GetAll() const = 0;
//##Documentation
//## @brief Convenience method to get the first node that matches the predicate condition
//##
DataNode *GetNode(const NodePredicateBase *condition = nullptr) const;
//##Documentation
//## @brief Convenience method to get the first node with a given name
//##
DataNode *GetNamedNode(const char *name) const;
//##Documentation
//## @brief Convenience method to get the first node with a given name
//##
DataNode *GetNamedNode(const std::string name) const { return this->GetNamedNode(name.c_str()); }
//##Documentation
//## @brief Convenience method to get the first node with a given name that is derived from sourceNode
//##
DataNode *GetNamedDerivedNode(const char *name,
const DataNode *sourceNode,
bool onlyDirectDerivations = true) const;
//##Documentation
//## @brief Convenience method to get the first data object of a given data type with a given name
//##
template <class DataType>
DataType *GetNamedObject(const char *name) const
{
if (name == nullptr)
return nullptr;
DataNode *n = this->GetNamedNode(name);
if (n == nullptr)
return nullptr;
else
return dynamic_cast<DataType *>(n->GetData());
}
//##Documentation
//## @brief Convenience method to get the first data object of a given data type with a given name
//##
template <class DataType>
DataType *GetNamedObject(const std::string name) const
{
return this->GetNamedObject<DataType>(name.c_str());
}
//##Documentation
//## @brief Convenience method to get the first data object of a given data type with a given name that is derived
// from a specific node
//##
template <class DataType>
DataType *GetNamedDerivedObject(const char *name,
const DataNode *sourceNode,
bool onlyDirectDerivations = true) const
{
if (name == nullptr)
return nullptr;
DataNode *n = this->GetNamedDerivedNode(name, sourceNode, onlyDirectDerivations);
if (n == nullptr)
return nullptr;
else
return dynamic_cast<DataType *>(n->GetData());
}
//##Documentation
//## @brief Returns a list of used grouptags
//##
const DataNode::GroupTagList GetGroupTags() const;
/*ITK Mutex */
- mutable itk::SimpleFastMutexLock m_MutexOne;
+ mutable std::mutex m_MutexOne;
/* Public Events */
typedef Message1<const DataNode*> DataStorageEvent;
//##Documentation
//## @brief AddEvent is emitted whenever a new node has been added to the DataStorage.
//##
//## Observers should register to this event by calling myDataStorage->AddNodeEvent.AddListener(myObject,
// MyObject::MyMethod).
//## After registering, myObject->MyMethod() will be called every time a new node has been added to the DataStorage.
//## Observers should unregister by calling myDataStorage->AddNodeEvent.RemoveListener(myObject,
//MyObject::MyMethod).
//## Note: AddEvents are _not_ emitted if a node is added to DataStorage by adding it to the the underlying
//DataTree!
// member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef
// for
// a Message1 object which is thread safe
DataStorageEvent AddNodeEvent;
//##Documentation
//## @brief RemoveEvent is emitted directly before a node is removed from the DataStorage.
//##
//## Observers should register to this event by calling myDataStorage->RemoveNodeEvent.AddListener(myObject,
// MyObject::MyMethod).
//## After registering, myObject->MyMethod() will be called every time a new node has been added to the DataStorage.
//## Observers should unregister by calling myDataStorage->RemoveNodeEvent.RemoveListener(myObject,
// MyObject::MyMethod).
//## Note: RemoveEvents are also emitted if a node was removed from the DataStorage by deleting it from the
//underlying
// DataTree
// member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef
// for
// a Message1 object which is thread safe
DataStorageEvent RemoveNodeEvent;
//##Documentation
//## @brief ChangedEvent is emitted directly after a node was changed.
//##
//## Observers should register to this event by calling myDataStorage->ChangedNodeEvent.AddListener(myObject,
// MyObject::MyMethod).
//## After registering, myObject->MyMethod() will be called every time a new node has been changed.
//## Observers should unregister by calling myDataStorage->ChangedNodeEvent.RemoveListener(myObject,
// MyObject::MyMethod).
//## Internally the DataStorage listens to itk::ModifiedEvents on the nodes and forwards them
//## to the listeners of this event.
// member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef
// for
// a Message1 object which is thread safe
DataStorageEvent ChangedNodeEvent;
//##Documentation
//## @brief DeleteNodeEvent is emitted directly before a node is deleted.
//##
//## Observers should register to this event by calling myDataStorage->DeleteNodeEvent.AddListener(myObject,
// MyObject::MyMethod).
//## After registering, myObject->MyMethod() will be called when a node is deleted.
//## Observers should unregister by calling myDataStorage->DeleteNodeEvent.RemoveListener(myObject,
// MyObject::MyMethod).
//## Internally the DataStorage listens to itk::DeleteEvents on the nodes and forwards them
//## to the listeners of this event.
// member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef
// for
// a Message1 object which is thread safe
DataStorageEvent DeleteNodeEvent;
DataStorageEvent InteractorChangedNodeEvent;
//##Documentation
//## @brief Compute the axis-parallel bounding geometry of the input objects
//##
//## Throws std::invalid_argument exception if input is nullptr
//## @param input set of objects of the DataStorage to be included in the bounding geometry
//## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer)
//## and is set to @a false, the node is ignored for the bounding-box calculation.
//## @param renderer see @a boolPropertyKey
//## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey
TimeGeometry::ConstPointer ComputeBoundingGeometry3D(const SetOfObjects *input,
const char *boolPropertyKey = nullptr,
const BaseRenderer *renderer = nullptr,
const char *boolPropertyKey2 = nullptr) const;
//##Documentation
//## @brief Compute the axis-parallel bounding geometry of the data tree
//## (bounding box, minimal spacing of the considered nodes, live-span)
//##
//## it -> an iterator to a data tree structure
//## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer)
//## and is set to @a false, the node is ignored for the bounding-box calculation.
//## @param renderer see @a boolPropertyKey
//## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey
TimeGeometry::ConstPointer ComputeBoundingGeometry3D(const char *boolPropertyKey = nullptr,
const BaseRenderer *renderer = nullptr,
const char *boolPropertyKey2 = nullptr) const;
//##Documentation
//## @brief Compute the axis-parallel bounding geometry of all visible parts of the
//## data tree bounding box, minimal spacing of the considered nodes, live-span)
//##
//## Simply calls ComputeBoundingGeometry3D(it, "visible", renderer, boolPropertyKey).
//## it -> an iterator of a data tree structure
//## @param renderer the reference to the renderer
//## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer)
//## and is set to @a false, the node is ignored for the bounding-box calculation.
TimeGeometry::ConstPointer ComputeVisibleBoundingGeometry3D(const BaseRenderer *renderer = nullptr,
const char *boolPropertyKey = nullptr);
//##Documentation
//## @brief Compute the bounding box of data tree structure
//## it -> an iterator to a data tree structure
//## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer)
//## and is set to @a false, the node is ignored for the bounding-box calculation.
//## @param renderer see @a boolPropertyKey
//## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey
BoundingBox::Pointer ComputeBoundingBox(const char *boolPropertyKey = nullptr,
const BaseRenderer *renderer = nullptr,
const char *boolPropertyKey2 = nullptr);
//##Documentation
//## \brief Compute the bounding box of all visible parts of the data tree structure, for general
//## rendering or renderer specific visibility property checking
//##
//## Simply calls ComputeBoundingBox(it, "visible", renderer, boolPropertyKey).
//## it -> an iterator of a data tree structure
//## @param renderer the reference to the renderer
//## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer)
//## and is set to @a false, the node is ignored for the bounding-box calculation.
BoundingBox::Pointer ComputeVisibleBoundingBox(const BaseRenderer *renderer = nullptr,
const char *boolPropertyKey = nullptr)
{
return ComputeBoundingBox("visible", renderer, boolPropertyKey);
}
//##Documentation
//## @brief Compute the time-bounds of the contents of a data tree structure
//##
//## The methods returns only [-infinity, +infinity], if all data-objects have an infinite live-span. Otherwise,
//## all data-objects with infinite live-span are ignored.
//## it -> an iterator to a data tree structure
//## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer)
//## and is set to @a false, the node is ignored for the time-bounds calculation.
//## @param renderer see @a boolPropertyKey
//## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey
TimeBounds ComputeTimeBounds(const char *boolPropertyKey,
const BaseRenderer *renderer,
const char *boolPropertyKey2);
//##Documentation
//## @brief Compute the time-bounds of all visible parts of the data tree structure, for general
//## rendering or renderer specific visibility property checking
//##
//## The methods returns only [-infinity, +infinity], if all data-objects have an infinite live-span. Otherwise,
//## all data-objects with infinite live-span are ignored.
//## Simply calls ComputeTimeBounds(it, "visible", renderer, boolPropertyKey).
//## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer)
//## and is set to @a false, the node is ignored for the time-bounds calculation.
//## @param renderer see @a boolPropertyKey
TimeBounds ComputeTimeBounds(const BaseRenderer *renderer, const char *boolPropertyKey)
{
return ComputeTimeBounds("visible", renderer, boolPropertyKey);
}
//##Documentation
//## @brief Defines whether or not NodeChangedEvent is invoked .
//##
//## This method can be used to set m_BlockNodeModifiedEvents.
//##
//## If this flag is true, NodeChangedEvent is not invoked when a
//## DataNode is modified. This might be undesired when setting
//## many properties on a datanode and you do not want anyone to
//## react.
void BlockNodeModifiedEvents(bool block);
protected:
//##Documentation
//## @brief EmitAddNodeEvent emits the AddNodeEvent
//##
//## This method should be called by subclasses to emit the AddNodeEvent
void EmitAddNodeEvent(const DataNode *node);
//##Documentation
//## @brief EmitRemoveNodeEvent emits the RemoveNodeEvent
//##
//## This method should be called by subclasses to emit the RemoveNodeEvent
void EmitRemoveNodeEvent(const DataNode *node);
void OnNodeInteractorChanged(itk::Object *caller, const itk::EventObject &event);
//##Documentation
//## @brief OnNodeModified listens to modified events of DataNodes.
//##
//## The node is hidden behind the caller parameter, which has to be casted first.
//## If the cast succeeds the ChangedNodeEvent is emitted with this node.
void OnNodeModifiedOrDeleted(const itk::Object *caller, const itk::EventObject &event);
//##Documentation
//## @brief Adds a Modified-Listener to the given Node.
void AddListeners(const DataNode *_Node);
//##Documentation
//## @brief Removes a Modified-Listener from the given Node.
void RemoveListeners(const DataNode *_Node);
//##Documentation
//## @brief Saves Modified-Observer Tags for each node in order to remove the event listeners again.
std::map<const DataNode *, unsigned long> m_NodeModifiedObserverTags;
std::map<const DataNode *, unsigned long> m_NodeInteractorChangedObserverTags;
//##Documentation
//## @brief Saves Delete-Observer Tags for each node in order to remove the event listeners again.
std::map<const DataNode *, unsigned long> m_NodeDeleteObserverTags;
//##Documentation
//## @brief If this class changes nodes itself, set this to TRUE in order
//## to suppress NodeChangedEvent to be emitted.
bool m_BlockNodeModifiedEvents;
DataStorage();
~DataStorage() override;
//##Documentation
//## @brief Filters a SetOfObjects by the condition. If no condition is provided, the original set is returned
SetOfObjects::ConstPointer FilterSetOfObjects(const SetOfObjects *set, const NodePredicateBase *condition) const;
//##Documentation
//## @brief Prints the contents of the DataStorage to os. Do not call directly, call ->Print() instead
void PrintSelf(std::ostream &os, itk::Indent indent) const override;
};
//##Documentation
//## @brief returns the topmost visible node of a given list of nodes.
//## The function returns a node that is visible and has the highest layer of a set of given nodes.
//## The property list, which is used to find the visibility- and layer-property is is specified by the
//## given base renderer.
//##
MITKCORE_EXPORT DataNode::Pointer FindTopmostVisibleNode(const DataStorage::SetOfObjects::ConstPointer nodes,
const Point3D worldPosition,
const TimePointType timePoint,
const BaseRenderer* baseRender);
} // namespace mitk
#endif // MITKDATASTORAGE_H
diff --git a/Modules/Core/include/mitkEqual.h b/Modules/Core/include/mitkEqual.h
index ad6d0b9745..af3b911269 100644
--- a/Modules/Core/include/mitkEqual.h
+++ b/Modules/Core/include/mitkEqual.h
@@ -1,84 +1,85 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
/*
* mitkEqual.h
*
* Created on: Apr 14, 2014
* Author: wirkert
*/
#ifndef MITKEQUAL_H_
#define MITKEQUAL_H_
+#include <cmath>
#include <iomanip>
#include "mitkLogMacros.h"
#include "mitkNumericConstants.h"
namespace mitk
{
/**
* Helper method to check if the difference is bigger or equal to a given epsilon
*
* @param diff the difference to be checked against the epsilon
* @param epsilon The absolute difference needs to be smaller than this.
* @return true if abs(diff) >= eps
*/
template <typename DifferenceType>
inline bool DifferenceBiggerOrEqualEps(DifferenceType diff, mitk::ScalarType epsilon = mitk::eps)
{
- return fabs(diff) >= epsilon;
+ return std::fabs(diff) >= epsilon;
}
/**
* outputs elem1, elem2 and eps in case verbose and !isEqual.
* Elem can e.g. be a mitk::Vector or an mitk::Point.
*
* @param elem1 first element to be output
* @param elem2 second
* @param eps the epsilon which their difference was bigger than
* @param verbose tells the function if something shall be output
* @param isEqual function will only output something if the two elements are not equal
*/
template <typename ElementToOutput1, typename ElementToOutput2>
inline void ConditionalOutputOfDifference(
ElementToOutput1 elem1, ElementToOutput2 elem2, mitk::ScalarType eps, bool verbose, bool isEqual)
{
if (verbose && !isEqual)
{
MITK_INFO << typeid(ElementToOutput1).name() << " and " << typeid(ElementToOutput2).name()
<< " not equal. Lefthandside " << std::setprecision(12) << elem1 << " - Righthandside " << elem2
<< " - epsilon " << eps;
}
}
/**
* @ingroup MITKTestingAPI
*
* @param scalar1 Scalar value to compare.
* @param scalar2 Scalar value to compare.
* @param eps Tolerance for floating point comparison.
* @param verbose Flag indicating detailed console output.
* @return True if scalars are equal.
*/
inline bool Equal(ScalarType scalar1, ScalarType scalar2, ScalarType eps = mitk::eps, bool verbose = false)
{
bool isEqual(!DifferenceBiggerOrEqualEps(scalar1 - scalar2, eps));
ConditionalOutputOfDifference(scalar1, scalar2, eps, verbose, isEqual);
return isEqual;
}
}
#endif /* MITKEQUAL_H_ */
diff --git a/Modules/Core/include/mitkExtractSliceFilter2.h b/Modules/Core/include/mitkExtractSliceFilter2.h
index 60303f435c..5366ccbc95 100644
--- a/Modules/Core/include/mitkExtractSliceFilter2.h
+++ b/Modules/Core/include/mitkExtractSliceFilter2.h
@@ -1,80 +1,80 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkExtractSliceFilter2_h
#define mitkExtractSliceFilter2_h
#include <mitkImageToImageFilter.h>
#include <mitkPlaneGeometry.h>
#include <MitkCoreExports.h>
namespace mitk
{
/** \brief Extract an arbitrarily oriented 2-d image from a 3-d image.
*
* Use ExtractSliceFilter2::SetOutputGeometry to specify the orientation of
* the 2-d output image.
*
* If a pixel of the 2-d output image isn't located within the bounds of the
* 3-d input image, it is set to the lowest possible pixel value.
*
* Cubic interpolation is considerably slow on the first update for a newly
* set input image. Subsequent filter updates with cubic interpolation are
* faster by several orders of magnitude as long as the input image was
* neither changed nor modified.
*
* This filter is completely based on ITK compared to the VTK-based
* mitk::ExtractSliceFilter. It is more robust, easy to use, and produces
* an mitk::Image with valid geometry. Generally it is not as fast as
* mitk::ExtractSliceFilter, though.
*/
class MITKCORE_EXPORT ExtractSliceFilter2 final : public ImageToImageFilter
{
public:
enum Interpolator
{
NearestNeighbor,
Linear,
Cubic
};
mitkClassMacro(ExtractSliceFilter2, ImageToImageFilter);
itkFactorylessNewMacro(Self);
void SetInput(const InputImageType* image) override;
void SetInput(unsigned int index, const InputImageType* image) override;
const PlaneGeometry* GetOutputGeometry() const;
void SetOutputGeometry(PlaneGeometry::Pointer outputGeometry);
Interpolator GetInterpolator() const;
void SetInterpolator(Interpolator interpolator);
private:
using Superclass::SetInput;
ExtractSliceFilter2();
~ExtractSliceFilter2() override;
void AllocateOutputs() override;
// void BeforeThreadedGenerateData() override;
// void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, itk::ThreadIdType threadId) override;
void GenerateData() override;
- void VerifyInputInformation() override;
+ void VerifyInputInformation() const override;
struct Impl;
Impl* m_Impl;
};
}
#endif
diff --git a/Modules/Core/include/mitkIOUtil.h b/Modules/Core/include/mitkIOUtil.h
index 946016eb9c..24f878efa2 100644
--- a/Modules/Core/include/mitkIOUtil.h
+++ b/Modules/Core/include/mitkIOUtil.h
@@ -1,456 +1,434 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKIOUTIL_H
#define MITKIOUTIL_H
#include <MitkCoreExports.h>
#include <mitkDataStorage.h>
#include <mitkImage.h>
#include <mitkPointSet.h>
#include <mitkSurface.h>
#include <mitkFileReaderSelector.h>
#include <mitkFileWriterSelector.h>
#include <mitkIFileReader.h>
#include <mitkIFileWriter.h>
#include <fstream>
namespace us
{
class ModuleResource;
}
namespace mitk
{
/**
* \ingroup IO
*
* \brief A utility class to load and save data from/to the local file system.
*
* \see QmitkIOUtil
*/
class MITKCORE_EXPORT IOUtil
{
public:
/**Struct that containes information regarding the current loading process. (e.g. Path that should be loaded,
all found readers for the load path,...). It is set be IOUtil and used to pass information via the option callback
in load operations.
*/
struct MITKCORE_EXPORT LoadInfo
{
LoadInfo(const std::string &path);
std::string m_Path;
std::vector<BaseData::Pointer> m_Output;
FileReaderSelector m_ReaderSelector;
bool m_Cancel;
};
/**Struct that is the base class for option callbacks used in load operations. The callback is used by IOUtil, if
more than one suitable reader was found or the a reader containes options that can be set. The callback allows to
change option settings and select the reader that should be used (via loadInfo).
*/
struct MITKCORE_EXPORT ReaderOptionsFunctorBase
{
virtual bool operator()(LoadInfo &loadInfo) const = 0;
};
struct MITKCORE_EXPORT SaveInfo
{
SaveInfo(const BaseData *baseData, const MimeType &mimeType, const std::string &path);
bool operator<(const SaveInfo &other) const;
/// The BaseData object to save.
const BaseData *m_BaseData;
/// Contains a set of IFileWriter objects.
FileWriterSelector m_WriterSelector;
/// The selected mime-type, used to restrict results from FileWriterSelector.
MimeType m_MimeType;
/// The path to write the BaseData object to.
std::string m_Path;
/// Flag indicating if sub-sequent save operations are to be canceled.
bool m_Cancel;
};
/**Struct that is the base class for option callbacks used in save operations. The callback is used by IOUtil, if
more than one suitable writer was found or the a writer containes options that can be set. The callback allows to
change option settings and select the writer that should be used (via saveInfo).
*/
struct MITKCORE_EXPORT WriterOptionsFunctorBase
{
virtual bool operator()(SaveInfo &saveInfo) const = 0;
};
/**
* Get the file system path where the running executable is located.
*
* @return The location of the currently running executable, without the filename.
*/
static std::string GetProgramPath();
/**
* Get the default temporary path.
*
* @return The default path for temporary data.
*/
static std::string GetTempPath();
/**
* Returns the Directory Seperator for the current OS.
*
* @return the Directory Seperator for the current OS, i.e. "\\" for Windows and "/" otherwise.
*/
static char GetDirectorySeparator();
/**
* Create and open a temporary file.
*
* This method generates a unique temporary filename from \c templateName, creates
* and opens the file using the output stream \c tmpStream and returns the name of
* the newly create file.
*
* The \c templateName argument must contain six consective 'X' characters ("XXXXXX")
* and these are replaced with a string that makes the filename unique.
*
* The file is created with read and write permissions for owner only.
*
* @param tmpStream The output stream for writing to the temporary file.
* @param templateName An optional template for the filename.
* @param path An optional path where the temporary file should be created. Defaults
* to the default temp path as returned by GetTempPath().
* @return The filename of the created temporary file.
*
* @throw mitk::Exception if the temporary file could not be created.
*/
static std::string CreateTemporaryFile(std::ofstream &tmpStream,
const std::string &templateName = "XXXXXX",
std::string path = std::string());
/**
* Create and open a temporary file.
*
* This method generates a unique temporary filename from \c templateName, creates
* and opens the file using the output stream \c tmpStream and the specified open
* mode \c mode and returns the name of the newly create file. The open mode is always
* OR'd with <code>std::ios_base::out | std::ios_base::trunc</code>.
*
* The \c templateName argument must contain six consective 'X' characters ("XXXXXX")
* and these are replaced with a string that makes the filename unique.
*
* The file is created with read and write permissions for owner only.
*
* @param tmpStream The output stream for writing to the temporary file.
* @param mode The open mode for the temporary file stream.
* @param templateName An optional template for the filename.
* @param path An optional path where the temporary file should be created. Defaults
* to the default temp path as returned by GetTempPath().
* @return The filename of the created temporary file.
*
* @throw mitk::Exception if the temporary file could not be created.
*/
static std::string CreateTemporaryFile(std::ofstream &tmpStream,
std::ios_base::openmode mode,
const std::string &templateName = "XXXXXX",
std::string path = std::string());
/**
* Creates an empty temporary file.
*
* This method generates a unique temporary filename from \c templateName and creates
* this file.
*
* The file is created with read and write permissions for owner only.
*
* ---
* This version is potentially unsafe because the created temporary file is not kept open
* and could be used by another process between calling this method and opening the returned
* file path for reading or writing.
* ---
*
* @return The filename of the created temporary file.
* @param templateName An optional template for the filename.
* @param path An optional path where the temporary file should be created. Defaults
* to the default temp path as returned by GetTempPath().
* @throw mitk::Exception if the temporary file could not be created.
*/
static std::string CreateTemporaryFile(const std::string &templateName = "XXXXXX",
std::string path = std::string());
/**
* Create a temporary directory.
*
* This method generates a uniquely named temporary directory from \c templateName.
* The last set of six consecutive 'X' characters in \c templateName is replaced
* with a string that makes the directory name unique.
*
* The directory is created with read, write and executable permissions for owner only.
*
* @param templateName An optional template for the directory name.
* @param path An optional path where the temporary directory should be created. Defaults
* to the default temp path as returned by GetTempPath().
* @return The filename of the created temporary file.
*
* @throw mitk::Exception if the temporary directory could not be created.
*/
static std::string CreateTemporaryDirectory(const std::string &templateName = "XXXXXX",
std::string path = std::string());
/**
* @brief Load a file into the given DataStorage.
*
* This method calls Load(const std::vector<std::string>&, DataStorage&) with a
* one-element vector.
*
* @param path The absolute file name including the file extension.
* @param storage A DataStorage object to which the loaded data will be added.
* @param optionsCallback Pointer to a callback instance. The callback is used by
* the load operation if more the suitable reader was found or the reader has options
* that can be set.
* @return The set of added DataNode objects.
* @throws mitk::Exception if \c path could not be loaded.
*
* @sa Load(const std::vector<std::string>&, DataStorage&)
*/
static DataStorage::SetOfObjects::Pointer Load(const std::string &path, DataStorage &storage,
const ReaderOptionsFunctorBase *optionsCallback = nullptr);
/**
* @brief Load a file into the given DataStorage given user defined IFileReader::Options.
*
* This method calls Load(const std::vector<std::string>&, DataStorage&) with a
* one-element vector.
*
* @param path The absolute file name including the file extension.
* @param options IFileReader option instance that should be used if selected reader
* has options.
* @param storage A DataStorage object to which the loaded data will be added.
* @return The set of added DataNode objects.
* @throws mitk::Exception if \c path could not be loaded.
*
* @sa Load(const std::vector<std::string>&, DataStorage&)
*/
static DataStorage::SetOfObjects::Pointer Load(const std::string &path,
const IFileReader::Options &options,
DataStorage &storage);
/**
* @brief Load a file and return the loaded data.
*
* This method calls Load(const std::vector<std::string>&) with a
* one-element vector.
*
* @param path The absolute file name including the file extension.
* @param optionsCallback Pointer to a callback instance. The callback is used by
* the load operation if more the suitable reader was found or the reader has options
* that can be set.
* @return The set of added DataNode objects.
* @throws mitk::Exception if \c path could not be loaded.
*
* @sa Load(const std::vector<std::string>&, DataStorage&)
*/
static std::vector<BaseData::Pointer> Load(const std::string &path,
const ReaderOptionsFunctorBase *optionsCallback = nullptr);
template <typename T>
static typename T::Pointer Load(const std::string& path, const ReaderOptionsFunctorBase *optionsCallback = nullptr)
{
return dynamic_cast<T*>(Load(path, optionsCallback).at(0).GetPointer());
}
/**
* @brief Load a file and return the loaded data.
*
* This method calls Load(const std::vector<std::string>&) with a
* one-element vector.
*
* @param path The absolute file name including the file extension.
* @param options IFileReader option instance that should be used if selected reader
* has options.
* @return The set of added DataNode objects.
* @throws mitk::Exception if \c path could not be loaded.
*
* @sa Load(const std::vector<std::string>&, DataStorage&)
*/
static std::vector<BaseData::Pointer> Load(const std::string &path, const IFileReader::Options &options);
template <typename T>
static typename T::Pointer Load(const std::string& path, const IFileReader::Options &options)
{
return dynamic_cast<T*>(Load(path, options).at(0).GetPointer());
}
/**
* @brief Loads a list of file paths into the given DataStorage.
*
* If an entry in \c paths cannot be loaded, this method will continue to load
* the remaining entries into \c storage and throw an exception afterwards.
*
* @param paths A list of absolute file names including the file extension.
* @param storage A DataStorage object to which the loaded data will be added.
* @param optionsCallback Pointer to a callback instance. The callback is used by
* the load operation if more the suitable reader was found or the reader has options
* that can be set.
* @return The set of added DataNode objects.
* @throws mitk::Exception if an entry in \c paths could not be loaded.
*/
static DataStorage::SetOfObjects::Pointer Load(const std::vector<std::string> &paths, DataStorage &storage,
const ReaderOptionsFunctorBase *optionsCallback = nullptr);
static std::vector<BaseData::Pointer> Load(const std::vector<std::string> &paths,
const ReaderOptionsFunctorBase *optionsCallback = nullptr);
/**
* @brief Loads the contents of a us::ModuleResource and returns the corresponding mitk::BaseData
* @param usResource a ModuleResource, representing a BaseData object
* @param mode Optional parameter to set the openmode of the stream
* @return The set of loaded BaseData objects. \c Should contain either one or zero elements, since a resource
* stream
* respresents one object.
* @throws mitk::Exception if no reader was found for the stream.
*/
static std::vector<BaseData::Pointer> Load(const us::ModuleResource &usResource,
std::ios_base::openmode mode = std::ios_base::in);
template <typename T>
static typename T::Pointer Load(const us::ModuleResource &usResource, std::ios_base::openmode mode = std::ios_base::in)
{
return dynamic_cast<T*>(Load(usResource, mode).at(0).GetPointer());
}
/**
* @brief Save a mitk::BaseData instance.
* @param data The data to save.
* @param path The path to the image including file name and and optional file extension.
* If no extension is set, the default extension and mime-type for the
* BaseData type of \c data is used.
* @param setPathProperty
* @throws mitk::Exception if no writer for \c data is available or the writer
* is not able to write the image.
*/
static void Save(const mitk::BaseData *data, const std::string &path, bool setPathProperty = false);
/**
* @brief Save a mitk::BaseData instance.
* @param data The data to save.
* @param path The path to the image including file name and an optional file extension.
* If no extension is set, the default extension and mime-type for the
* BaseData type of \c data is used.
* @param options The IFileWriter options to use for the selected writer.
* @param setPathProperty
* @throws mitk::Exception if no writer for \c data is available or the writer
* is not able to write the image.
*/
static void Save(const mitk::BaseData *data, const std::string &path, const IFileWriter::Options &options, bool setPathProperty = false);
/**
* @brief Save a mitk::BaseData instance.
* @param data The data to save.
* @param mimeType The mime-type to use for writing \c data.
* @param path The path to the image including file name and an optional file extension.
* @param addExtension If \c true, an extension according to the given \c mimeType
* is added to \c path if it does not contain one. If \c path already contains
* a file name extension, it is not checked for compatibility with \c mimeType.
* @param setPathProperty
*
* @throws mitk::Exception if no writer for the combination of \c data and \c mimeType is
* available or the writer is not able to write the image.
*/
static void Save(const mitk::BaseData *data,
const std::string &mimeType,
const std::string &path,
bool addExtension = true,
bool setPathProperty = false);
/**
* @brief Save a mitk::BaseData instance.
* @param data The data to save.
* @param mimeType The mime-type to use for writing \c data.
* @param path The path to the image including file name and an optional file extension.
* @param options Configuration data for the used IFileWriter instance.
* @param addExtension If \c true, an extension according to the given \c mimeType
* is added to \c path if it does not contain one. If \c path already contains
* a file name extension, it is not checked for compatibility with \c mimeType.
* @param setPathProperty
*
* @throws mitk::Exception if no writer for the combination of \c data and \c mimeType is
* available or the writer is not able to write the image.
*/
static void Save(const mitk::BaseData *data,
const std::string &mimeType,
const std::string &path,
const mitk::IFileWriter::Options &options,
bool addExtension = true,
bool setPathProperty = false);
/**
* @brief Use SaveInfo objects to save BaseData instances.
*
* This is a low-level method for directly working with SaveInfo objects. Usually,
* the Save() methods taking a BaseData object as an argument are more appropriate.
*
* @param saveInfos A list of SaveInfo objects for saving contained BaseData objects.
* @param setPathProperty
*
* @see Save(const mitk::BaseData*, const std::string&)
*/
static void Save(std::vector<SaveInfo> &saveInfos, bool setPathProperty = false);
- /**
- * @brief Convert a string encoded with the current code page to an UTF-8 encoded string (Windows)
- *
- * The conversion happens on Windows only. On all other platforms, the input string
- * is returned unmodified as it is assumed to be UTF-8 encoded already.
- *
- * If the conversion fails, a warning is printed and the input string is returned
- * instead. This matches the behavior before this method was introduced.
- */
- static std::string Local8BitToUtf8(const std::string& local8BitStr);
-
- /**
- * @brief Convert a UTF-8 encoded string to a string encoded with the current code page (Windows)
- *
- * The conversion happens on Windows only. On all other platforms, the input string
- * is returned unmodified as strings are assumed to be always UTF-8 encoded by default.
- *
- * If the conversion fails, a warning is printed and the input string is returned
- * instead. This matches the behavior before this method was introduced.
- */
- static std::string Utf8ToLocal8Bit(const std::string& utf8Str);
-
protected:
static std::string Load(std::vector<LoadInfo> &loadInfos,
DataStorage::SetOfObjects *nodeResult,
DataStorage *ds,
const ReaderOptionsFunctorBase *optionsCallback);
static std::string Save(const BaseData *data,
const std::string &mimeType,
const std::string &path,
WriterOptionsFunctorBase *optionsCallback,
bool addExtension,
bool setPathProperty);
static std::string Save(std::vector<SaveInfo> &saveInfos,
WriterOptionsFunctorBase *optionsCallback,
bool setPathProperty);
private:
struct Impl;
};
}
#endif // MITKIOUTIL_H
diff --git a/Modules/Core/include/mitkImage.h b/Modules/Core/include/mitkImage.h
index c8b6f03464..a568fb65b8 100644
--- a/Modules/Core/include/mitkImage.h
+++ b/Modules/Core/include/mitkImage.h
@@ -1,643 +1,643 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2
#define MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2
#include "mitkBaseData.h"
#include "mitkImageAccessorBase.h"
#include "mitkImageDataItem.h"
#include "mitkImageDescriptor.h"
#include "mitkImageVtkAccessor.h"
#include "mitkLevelWindow.h"
#include "mitkPlaneGeometry.h"
#include "mitkSlicedData.h"
#include <MitkCoreExports.h>
#include <mitkProportionalTimeGeometry.h>
#ifndef __itkHistogram_h
#include <itkHistogram.h>
#endif
class vtkImageData;
namespace itk
{
template <class T>
class MutexLockHolder;
}
namespace mitk
{
class SubImageSelector;
class ImageTimeSelector;
class ImageStatisticsHolder;
/**
* @brief Image class for storing images
*
* Can be asked for header information, the data vector, 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 BaseGeometry of the Image, see
* BaseGeometry::WorldToItkPhysicalPoint.
*
* For more information, see \ref MitkImagePage .
* @ingroup Data
*/
class MITKCORE_EXPORT Image : public SlicedData
{
friend class SubImageSelector;
friend class ImageAccessorBase;
friend class ImageVtkAccessor;
friend class ImageVtkReadAccessor;
friend class ImageVtkWriteAccessor;
friend class ImageReadAccessor;
friend class ImageWriteAccessor;
public:
mitkClassMacro(Image, SlicedData);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** Smart Pointer type to a ImageDataItem. */
typedef itk::SmartPointer<ImageDataItem> ImageDataItemPointer;
typedef itk::Statistics::Histogram<double> HistogramType;
typedef mitk::ImageStatisticsHolder *StatisticsHolderPointer;
/** This enum is evaluated when setting new data to an image.
*/
enum ImportMemoryManagementType
{
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. */
ManageMemory, /**< Data to be set will be referenced, and Data memory block will be freed on deletion of mitk::Image. */
ReferenceMemory, /**< Data to be set will be referenced, but Data memory block will not be freed on deletion of mitk::Image. */
DontManageMemory = ReferenceMemory
};
/**
* @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<ImageDataItemPointer> ImageDataItemPointerArray;
public:
/**
* @brief Returns the PixelType of channel @a n.
*/
const mitk::PixelType GetPixelType(int n = 0) const;
/**
* @brief Get dimension of the image
*/
unsigned int GetDimension() const;
/**
* @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;
public:
/**
* @brief Get a volume at a specific time @a t of channel @a n as a vtkImageData.
*/
virtual vtkImageData *GetVtkImageData(int t = 0, int n = 0);
virtual const vtkImageData *GetVtkImageData(int t = 0, int n = 0) const;
/**
* @brief Check whether slice @a s at time @a t in channel @a n is set
*/
bool IsSliceSet(int s = 0, int t = 0, int n = 0) const override;
/**
* @brief Check whether volume at time @a t in channel @a n is set
*/
bool IsVolumeSet(int t = 0, int n = 0) const override;
/**
* @brief Check whether the channel @a n is set
*/
bool IsChannelSet(int n = 0) const override;
/**
* @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);
/**
* @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);
/**
* @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);
/**
* @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);
/**
* @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);
virtual bool SetImportVolume(const void *const_data, int t = 0, int n = 0);
/**
* @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);
/**
* 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);
/**
* initialize new (or re-initialize) image information by a BaseGeometry
*
* \param type
* \param geometry
* \param channels
* @param tDim defines the number of time steps for which the Image should be initialized
*/
virtual void Initialize(const mitk::PixelType &type,
const mitk::BaseGeometry &geometry,
unsigned int channels = 1,
int tDim = 1);
/**
* \brief Initialize new (or re-initialize) image information by a TimeGeometry
*
* \param type
* \param geometry
* \param channels
* \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);
/**
* initialize new (or re-initialize) image information by a PlaneGeometry and number of slices
*
* Initializes the bounding box according to the width/height of the
* PlaneGeometry and @a sDim via SlicedGeometry3D::InitializeEvenlySpaced.
* The spacing is calculated from the PlaneGeometry.
* \sa SlicedGeometry3D::InitializeEvenlySpaced
*/
virtual void Initialize(const mitk::PixelType &type,
int sDim,
const mitk::PlaneGeometry &geometry2d,
unsigned int channels = 1,
int tDim = 1);
/**
* 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);
/**
* 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 vtkimagedata
* @param channels
* @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);
/**
* 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 itkimage
* @param channels
* @param tDim override time dimension in @a itkimage (if >0 and <)
* @param sDim override z-space dimension in @a itkimage (if >0 and <)
*/
template <typename itkImageType>
void InitializeByItk(const itkImageType *itkimage, int channels = 1, int tDim = -1, int sDim = -1)
{
if (itkimage == nullptr)
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; i < m_Dimension; ++i)
tmpDimensions[i] = itkimage->GetLargestPossibleRegion().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<itkImageType>(itkimage->GetNumberOfComponentsPerPixel()), 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<PlaneGeometry *>(GetSlicedGeometry(0)->GetPlaneGeometry(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();
}
/**
* @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;
/**
* @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;
/**
* @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;
/**
* @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;
/**
* @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.
*/
void SetGeometry(BaseGeometry *aGeometry3D) override;
/**
* @warning for internal use only
*/
virtual ImageDataItemPointer GetSliceData(int s = 0,
int t = 0,
int n = 0,
void *data = nullptr,
ImportMemoryManagementType importMemoryManagement = CopyMemory) const;
/**
* @warning for internal use only
*/
virtual ImageDataItemPointer GetVolumeData(int t = 0,
int n = 0,
void *data = nullptr,
ImportMemoryManagementType importMemoryManagement = CopyMemory) const;
/**
* @warning for internal use only
*/
virtual ImageDataItemPointer GetChannelData(int n = 0,
void *data = nullptr,
ImportMemoryManagementType importMemoryManagement = CopyMemory) 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:
mitkCloneMacro(Self);
- typedef itk::MutexLockHolder<itk::SimpleFastMutexLock> MutexHolder;
+ typedef std::lock_guard<std::mutex> MutexHolder;
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;
void Expand(unsigned int timeSteps) override;
virtual ImageDataItemPointer AllocateSliceData(
int s = 0,
int t = 0,
int n = 0,
void *data = nullptr,
ImportMemoryManagementType importMemoryManagement = CopyMemory) const;
virtual ImageDataItemPointer AllocateVolumeData(
int t = 0, int n = 0, void *data = nullptr, ImportMemoryManagementType importMemoryManagement = CopyMemory) const;
virtual ImageDataItemPointer AllocateChannelData(
int n = 0, void *data = nullptr, ImportMemoryManagementType importMemoryManagement = CopyMemory) const;
Image();
Image(const Image &other);
~Image() override;
void Clear() override;
/** @warning Has to be called by every Initialize method! */
void Initialize() override;
void PrintSelf(std::ostream &os, itk::Indent indent) const override;
mutable ImageDataItemPointerArray m_Channels;
mutable ImageDataItemPointerArray m_Volumes;
mutable ImageDataItemPointerArray m_Slices;
- mutable itk::SimpleFastMutexLock m_ImageDataArraysLock;
+ mutable std::mutex m_ImageDataArraysLock;
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:
ImageDataItemPointer GetSliceData_unlocked(
int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const;
ImageDataItemPointer GetVolumeData_unlocked(int t,
int n,
void *data,
ImportMemoryManagementType importMemoryManagement) const;
ImageDataItemPointer GetChannelData_unlocked(int n,
void *data,
ImportMemoryManagementType importMemoryManagement) const;
ImageDataItemPointer AllocateSliceData_unlocked(
int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const;
ImageDataItemPointer AllocateVolumeData_unlocked(int t,
int n,
void *data,
ImportMemoryManagementType importMemoryManagement) const;
ImageDataItemPointer AllocateChannelData_unlocked(int n,
void *data,
ImportMemoryManagementType importMemoryManagement) const;
bool IsSliceSet_unlocked(int s, int t, int n) const;
bool IsVolumeSet_unlocked(int t, int n) const;
bool IsChannelSet_unlocked(int n) const;
/** Stores all existing ImageReadAccessors */
mutable std::vector<ImageAccessorBase *> m_Readers;
/** Stores all existing ImageWriteAccessors */
mutable std::vector<ImageAccessorBase *> m_Writers;
/** Stores all existing ImageVtkAccessors */
mutable std::vector<ImageAccessorBase *> m_VtkReaders;
/** A mutex, which needs to be locked to manage m_Readers and m_Writers */
- itk::SimpleFastMutexLock m_ReadWriteLock;
+ mutable std::mutex m_ReadWriteLock;
/** A mutex, which needs to be locked to manage m_VtkReaders */
- itk::SimpleFastMutexLock m_VtkReadersLock;
+ mutable std::mutex 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
*/
MITKCORE_EXPORT bool Equal(const mitk::Image &leftHandSide,
const mitk::Image &rightHandSide,
ScalarType eps,
bool verbose);
} // namespace mitk
#endif /* MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 */
diff --git a/Modules/Core/include/mitkImageAccessorBase.h b/Modules/Core/include/mitkImageAccessorBase.h
index e80978f435..a0419cc23d 100644
--- a/Modules/Core/include/mitkImageAccessorBase.h
+++ b/Modules/Core/include/mitkImageAccessorBase.h
@@ -1,160 +1,160 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKIMAGEACCESSORBASE_H
#define MITKIMAGEACCESSORBASE_H
#include <itkImageRegion.h>
#include <itkIndex.h>
-#include <itkMultiThreader.h>
-#include <itkSimpleFastMutexLock.h>
#include <itkSmartPointer.h>
#include "mitkImageDataItem.h"
+#include <mutex>
+
namespace mitk
{
//##Documentation
//## @brief The ImageAccessorBase class provides a lock mechanism for all inheriting image accessors.
//##
//## @ingroup Data
class Image;
/** \brief This struct allows to make ImageAccessors wait for this particular ImageAccessor object*/
struct ImageAccessorWaitLock
{
/** \brief Holds the number of ImageAccessors, which are waiting until the represented ImageAccessor is released. */
unsigned int m_WaiterCount;
/** \brief A mutex that allows other ImageAccessors to wait for the represented ImageAccessor. */
- itk::SimpleFastMutexLock m_Mutex;
+ std::mutex m_Mutex;
};
// Defs to assure dead lock prevention only in case of possible thread handling.
#if defined(ITK_USE_SPROC) || defined(ITK_USE_PTHREADS) || defined(ITK_USE_WIN32_THREADS)
#define MITK_USE_RECURSIVE_MUTEX_PREVENTION
#endif
class MITKCORE_EXPORT ImageAccessorBase
{
friend class Image;
friend class ImageReadAccessor;
friend class ImageWriteAccessor;
template <class TPixel, unsigned int VDimension>
friend class ImagePixelReadAccessor;
template <class TPixel, unsigned int VDimension>
friend class ImagePixelWriteAccessor;
public:
typedef itk::SmartPointer<const mitk::Image> ImageConstPointer;
/** \brief defines different flags for the ImageAccessor constructors
*/
enum Options
{
/** No specific Options ==> Default */
DefaultBehavior = 0,
/** Defines if the Constructor waits for locked memory until it is released or not. If not, an exception is
thrown.*/
ExceptionIfLocked = 1,
/** Defines if requested Memory has to be coherent. If the parameter is true, it is possible that new Memory has
to
be allocated to arrange this desired condition. Consequently, this parameter can heavily affect computation
time.*/
ForceCoherentMemory = 2,
/** Ignores the lock mechanism for immediate access. Only possible with read accessors. */
IgnoreLock = 4
};
virtual ~ImageAccessorBase();
/** \brief Gives const access to the data. */
inline const void *GetData() const { return m_AddressBegin; }
protected:
// Define type of thread id
#ifdef ITK_USE_SPROC
typedef int ThreadIDType;
#endif
#ifdef ITK_USE_WIN32_THREADS
typedef DWORD ThreadIDType;
#endif
#ifdef ITK_USE_PTHREADS
typedef pthread_t ThreadIDType;
#endif
/** \brief Checks validity of given parameters from inheriting classes and stores those parameters in member
* variables. */
ImageAccessorBase(ImageConstPointer iP, const ImageDataItem *iDI = nullptr, int OptionFlags = DefaultBehavior);
/** ImageAccessor has access to the image it belongs to. */
// ImagePointer m_Image;
/** Contains a SubRegion (always represented in maximal possible dimension) */
itk::ImageRegion<4> *m_SubRegion;
/** Points to the beginning of the image part. */
void *m_AddressBegin;
/** Contains the first address after the image part. */
void *m_AddressEnd;
/** \brief Stores all extended properties of an ImageAccessor.
* The different flags in mitk::ImageAccessorBase::Options can be unified by bitwise operations.
*/
int m_Options;
/** Defines if the accessed image part lies coherently in memory */
bool m_CoherentMemory;
/** \brief Pointer to a WaitLock struct, that allows other ImageAccessors to wait for this ImageAccessor */
ImageAccessorWaitLock *m_WaitLock;
/** \brief Increments m_WaiterCount. A call of this method is prohibited unless the Mutex m_ReadWriteLock in the
* mitk::Image class is Locked. */
inline void Increment() { m_WaitLock->m_WaiterCount += 1; }
/** \brief Computes if there is an Overlap of the image part between this instantiation and another ImageAccessor
* object
* \throws mitk::Exception if memory area is incoherent (not supported yet)
*/
bool Overlap(const ImageAccessorBase *iAB);
/** \brief Uses the WaitLock to wait for another ImageAccessor*/
void WaitForReleaseOf(ImageAccessorWaitLock *wL);
ThreadIDType m_Thread;
/** \brief Prevents a recursive mutex lock by comparing thread ids of competing image accessors */
void PreventRecursiveMutexLock(ImageAccessorBase *iAB);
virtual const Image *GetImage() const = 0;
private:
/** \brief System dependend thread method, to prevent recursive mutex access */
ThreadIDType CurrentThreadHandle();
/** \brief System dependend thread method, to prevent recursive mutex access */
inline bool CompareThreadHandles(ThreadIDType, ThreadIDType);
};
class MemoryIsLockedException : public Exception
{
public:
mitkExceptionClassMacro(MemoryIsLockedException, Exception)
};
}
#endif
diff --git a/Modules/Core/include/mitkImageGenerator.h b/Modules/Core/include/mitkImageGenerator.h
index 5642b6355b..fee6d12c9d 100644
--- a/Modules/Core/include/mitkImageGenerator.h
+++ b/Modules/Core/include/mitkImageGenerator.h
@@ -1,236 +1,236 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef ImageGenerator_H_HEADER_INCLUDED
#define ImageGenerator_H_HEADER_INCLUDED
#include "mitkImageWriteAccessor.h"
#include <MitkCoreExports.h>
#include <itkImageRegionIterator.h>
#include <itkMersenneTwisterRandomVariateGenerator.h>
#include <mitkImage.h>
namespace mitk
{
/**
* @brief generator for synthetic MITK images
* This is a helper class to generate synthetic MITK images (random or gradient).
*
* @ingroup IO
*/
class MITKCORE_EXPORT ImageGenerator
{
public:
/**
* \brief Generates gradient image with the defined size and spacing
*/
template <typename TPixelType>
static mitk::Image::Pointer GenerateGradientImage(unsigned int dimX,
unsigned int dimY,
unsigned int dimZ,
float spacingX = 1,
float spacingY = 1,
float spacingZ = 1)
{
typedef itk::Image<TPixelType, 3> ImageType;
typename ImageType::RegionType imageRegion;
imageRegion.SetSize(0, dimX);
imageRegion.SetSize(1, dimY);
imageRegion.SetSize(2, dimZ);
typename ImageType::SpacingType spacing;
spacing[0] = spacingX;
spacing[1] = spacingY;
spacing[2] = spacingZ;
mitk::Point3D origin;
origin.Fill(0.0);
itk::Matrix<double, 3, 3> directionMatrix;
directionMatrix.SetIdentity();
typename ImageType::Pointer image = ImageType::New();
image->SetSpacing(spacing);
image->SetOrigin(origin);
image->SetDirection(directionMatrix);
image->SetLargestPossibleRegion(imageRegion);
image->SetBufferedRegion(imageRegion);
image->SetRequestedRegion(imageRegion);
image->Allocate();
image->FillBuffer(0.0);
typedef itk::ImageRegionIterator<ImageType> IteratorOutputType;
IteratorOutputType it(image, imageRegion);
it.GoToBegin();
TPixelType val = 0;
while (!it.IsAtEnd())
{
it.Set(val);
val++;
++it;
}
mitk::Image::Pointer mitkImage = mitk::Image::New();
mitkImage->InitializeByItk(image.GetPointer());
mitkImage->SetVolume(image->GetBufferPointer());
return mitkImage;
}
/**
* \brief Generates an image of a same geometry as the one given as reference
The image buffer is filled to the fill_value given as parameter
*/
template <typename TPixelType>
static mitk::Image::Pointer GenerateImageFromReference(mitk::Image::Pointer reference, TPixelType fill_value)
{
mitk::Image::Pointer output = mitk::Image::New();
mitk::PixelType output_type = MakeScalarPixelType<TPixelType>();
// all metadata (except type) come from reference image
output->SetGeometry(reference->GetGeometry());
output->Initialize(output_type, reference->GetDimension(), reference->GetDimensions());
// get a pointer to the image buffer to write into
TPixelType *imageBuffer = nullptr;
try
{
mitk::ImageWriteAccessor writeAccess(output);
imageBuffer = static_cast<TPixelType *>(writeAccess.GetData());
}
catch (...)
{
MITK_ERROR << "Write access not granted on mitk::Image.";
}
// fill the buffer with the specifed value
for (unsigned int i = 0; i < output->GetVolumeData(0)->GetSize(); i++)
{
imageBuffer[i] = fill_value;
}
return output;
}
/*!
\brief Generates random image with the defined size and spacing
*/
template <typename TPixelType>
static mitk::Image::Pointer GenerateRandomImage(unsigned int dimX,
unsigned int dimY,
unsigned int dimZ = 1,
unsigned int dimT = 1,
mitk::ScalarType spacingX = 1,
mitk::ScalarType spacingY = 1,
mitk::ScalarType spacingZ = 1,
const double randomMax = 1000.0f,
const double randMin = 0.0f)
{
// set the data type according to the template
mitk::PixelType type = MakeScalarPixelType<TPixelType>();
// type.Initialize(typeid(TPixelType));
// initialize the MITK image with given dimenion and data type
mitk::Image::Pointer output = mitk::Image::New();
auto dimensions = new unsigned int[4];
unsigned int numberOfDimensions = 0;
unsigned int bufferSize = 0;
// check which dimension is needed
if (dimT <= 1)
{
if (dimZ <= 1)
{ // 2D
numberOfDimensions = 2;
dimensions[0] = dimX;
dimensions[1] = dimY;
bufferSize = dimX * dimY;
}
else
{ // 3D
numberOfDimensions = 3;
dimensions[0] = dimX;
dimensions[1] = dimY;
dimensions[2] = dimZ;
bufferSize = dimX * dimY * dimZ;
}
}
else
{ // 4D
numberOfDimensions = 4;
dimensions[0] = dimX;
dimensions[1] = dimY;
dimensions[2] = dimZ;
dimensions[3] = dimT;
bufferSize = dimX * dimY * dimZ * dimT;
}
output->Initialize(type, numberOfDimensions, dimensions);
mitk::Vector3D spacing;
spacing[0] = spacingX;
spacing[1] = spacingY;
spacing[2] = spacingZ;
output->SetSpacing(spacing);
// get a pointer to the image buffer to write into
TPixelType *imageBuffer = nullptr;
try
{
mitk::ImageWriteAccessor writeAccess(output);
imageBuffer = static_cast<TPixelType *>(writeAccess.GetData());
}
catch (...)
{
MITK_ERROR << "Write access not granted on mitk::Image.";
}
// initialize the random generator
itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer randomGenerator =
itk::Statistics::MersenneTwisterRandomVariateGenerator::New();
randomGenerator->Initialize();
// fill the buffer for each pixel/voxel
for (unsigned int i = 0; i < bufferSize; i++)
{
// the comparison of the component type is sufficient enough since the mitk::PixelType type object is
// created as SCALAR and hence does not need the comparison against type.GetPixelTypeId() ==
- // itk::ImageIOBase::SCALAR
- if (type.GetComponentType() == itk::ImageIOBase::INT) // call integer function
+ // itk::IOPixelEnum::SCALAR
+ if (type.GetComponentType() == itk::IOComponentEnum::INT) // call integer function
{
imageBuffer[i] = (TPixelType)randomGenerator->GetIntegerVariate((int)randomMax);
// TODO random generator does not support integer values in a given range (e.g. from 5-10)
// range is always [0, (int)randomMax]
}
- else if ((type.GetComponentType() == itk::ImageIOBase::DOUBLE) ||
- (type.GetComponentType() == itk::ImageIOBase::FLOAT)) // call floating point function
+ else if ((type.GetComponentType() == itk::IOComponentEnum::DOUBLE) ||
+ (type.GetComponentType() == itk::IOComponentEnum::FLOAT)) // call floating point function
{
imageBuffer[i] = (TPixelType)randomGenerator->GetUniformVariate(randMin, randomMax);
}
- else if (type.GetComponentType() == itk::ImageIOBase::UCHAR)
+ else if (type.GetComponentType() == itk::IOComponentEnum::UCHAR)
{
// use the integer randomGenerator with mod 256 to generate unsigned char values
imageBuffer[i] = (unsigned char)((int)randomGenerator->GetIntegerVariate((int)randomMax)) % 256;
}
- else if (type.GetComponentType() == itk::ImageIOBase::USHORT)
+ else if (type.GetComponentType() == itk::IOComponentEnum::USHORT)
{
imageBuffer[i] = (unsigned short)((int)randomGenerator->GetIntegerVariate((int)randomMax)) % 65536;
}
else
{
MITK_ERROR << "Datatype not supported yet.";
// TODO call different methods for other datatypes
}
}
return output;
}
};
} // namespace mitk
#endif /* ImageGenerator_H_HEADER_INCLUDED */
diff --git a/Modules/Core/include/mitkImageSource.h b/Modules/Core/include/mitkImageSource.h
index d5a26274e8..52461c8159 100644
--- a/Modules/Core/include/mitkImageSource.h
+++ b/Modules/Core/include/mitkImageSource.h
@@ -1,251 +1,251 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef IMAGESOURCE_H_HEADER_INCLUDED_C1E7D6EC
#define IMAGESOURCE_H_HEADER_INCLUDED_C1E7D6EC
#include "mitkBaseDataSource.h"
#include "mitkImage.h"
#include <MitkCoreExports.h>
namespace mitk
{
/**
* @brief Superclass of all classes generating Images (instances of class
* Image) as output.
*
* In itk and vtk the generated result of a ProcessObject is only guaranteed
* to be up-to-date, when Update() of the ProcessObject or the generated
* DataObject is called immediately before access of the data stored in the
* DataObject. This is also true for subclasses of mitk::BaseProcess and thus
* for mitk::ImageSource. But there are also three access methods provided
* that guarantee an up-to-date result (by first calling Update and then
* returning the result of GetOutput()): GetData(), GetPic() and
* GetVtkImageData().
* @ingroup Process
*/
class MITKCORE_EXPORT ImageSource : public BaseDataSource
{
public:
mitkClassMacro(ImageSource, BaseDataSource);
/** @brief Method for creation through the object factory. */
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** @brief Some convenient typedefs. */
typedef mitk::Image OutputImageType;
typedef OutputImageType OutputType;
typedef OutputImageType::Pointer OutputImagePointer;
typedef SlicedData::RegionType OutputImageRegionType;
/**
* @brief Get the output data of this image source object.
*
* The output of this
* function is not valid until an appropriate Update() method has
* been called, either explicitly or implicitly. Both the filter
* itself and the data object have Update() methods, and both
* methods update the data. Here are three ways to use
* GetOutput() and make sure the data is valid. In these
* examples, \a image is a pointer to some Image object, and the
* particular ProcessObjects involved are filters. The same
* examples apply to non-image (e.g. Mesh) data as well.
*
* \code
* anotherFilter->SetInput( someFilter->GetOutput() );
* anotherFilter->Update();
* \endcode
*
* In this situation, \a someFilter and \a anotherFilter are said
* to constitute a \b pipeline.
*
* \code
* image = someFilter->GetOutput();
* image->Update();
* \endcode
*
* \code
* someFilter->Update();
* image = someFilter->GetOutput();
* \endcode
* (In the above example, the two lines of code can be in
* either order.)
*
* Note that Update() is not called automatically except within a
* pipeline as in the first example. When \b streaming (using a
* StreamingImageFilter) is activated, it may be more efficient to
* use a pipeline than to call Update() once for each filter in
* turn.
*
* For an image, the data generated is for the requested
* Region, which can be set using ImageBase::SetRequestedRegion().
* By default, the largest possible region is requested.
*
* For Filters which have multiple outputs of different types, the
* GetOutput() method assumes the output is of OutputImageType. For
* the GetOutput(DataObjectPointerArraySizeType) method, a dynamic_cast is performed
* incase the filter has outputs of different types or image
* types. Derived classes should have named get methods for these
* outputs.
*/
mitkBaseDataSourceGetOutputDeclarations
/** @brief Make a DataObject of the correct type to used as the specified
* output.
*
* Every ProcessObject subclass must be able to create a
* DataObject that can be used as a specified output. This method
* is automatically called when DataObject::DisconnectPipeline() is
* called. DataObject::DisconnectPipeline, disconnects a data object
* from being an output of its current source. When the data object
* is disconnected, the ProcessObject needs to construct a replacement
* output data object so that the ProcessObject is in a valid state.
* So DataObject::DisconnectPipeline eventually calls
* ProcessObject::MakeOutput. Note that MakeOutput always returns a
* SmartPointer to a DataObject. If a subclass of ImageSource has
* multiple outputs of different types, then that class must provide
* an implementation of MakeOutput(). */
itk::DataObject::Pointer
MakeOutput(DataObjectPointerArraySizeType idx) override;
/**
* This is a default implementation to make sure we have something.
* Once all the subclasses of ProcessObject provide an appopriate
* MakeOutput(), then ProcessObject::MakeOutput() can be made pure
* virtual.
*/
itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override;
virtual vtkImageData *GetVtkImageData();
virtual const vtkImageData *GetVtkImageData() const;
protected:
ImageSource();
~ImageSource() override {}
/** @brief A version of GenerateData() specific for image processing
* filters.
*
* This implementation will split the processing across
* multiple threads. The buffer is allocated by this method. Then
* the BeforeThreadedGenerateData() method is called (if
* provided). Then, a series of threads are spawned each calling
* ThreadedGenerateData(). After all the threads have completed
* processing, the AfterThreadedGenerateData() method is called (if
* provided). If an image processing filter cannot be threaded, the
* filter should provide an implementation of GenerateData(). That
* implementation is responsible for allocating the output buffer.
* If a filter an be threaded, it should NOT provide a
* GenerateData() method but should provide a ThreadedGenerateData()
* instead.
*
* \sa ThreadedGenerateData() */
void GenerateData() override;
/** @brief If an imaging filter can be implemented as a multithreaded
* algorithm, the filter will provide an implementation of
* ThreadedGenerateData().
*
* This superclass will automatically split
* the output image into a number of pieces, spawn multiple threads,
* and call ThreadedGenerateData() in each thread. Prior to spawning
* threads, the BeforeThreadedGenerateData() method is called. After
* all the threads have completed, the AfterThreadedGenerateData()
* method is called. If an image processing filter cannot support
* threading, that filter should provide an implementation of the
* GenerateData() method instead of providing an implementation of
* ThreadedGenerateData(). If a filter provides a GenerateData()
* method as its implementation, then the filter is responsible for
* allocating the output data. If a filter provides a
* ThreadedGenerateData() method as its implementation, then the
* output memory will allocated automatically by this superclass.
* The ThreadedGenerateData() method should only produce the output
* specified by "outputThreadRegion"
* parameter. ThreadedGenerateData() cannot write to any other
* portion of the output image (as this is responsibility of a
* different thread).
*
* \sa GenerateData(), SplitRequestedRegion() */
virtual void ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread, itk::ThreadIdType threadId);
/** @brief This method is intentionally left blank.
*
* ImageSource's need not
* Initialize their containers. The Image::Allocate() method (called
* from GenerateData()) will resize the container if more memory is
* needed. Otherwise, the memory can be reused.
*/
void PrepareOutputs() override;
/** @brief The GenerateData method normally allocates the buffers for all of the
* outputs of a filter.
*
* Some filters may want to override this default
* behavior. For example, a filter may have multiple outputs with
* varying resolution. Or a filter may want to process data in place by
* grafting its input to its output.*/
virtual void AllocateOutputs();
/** @brief If an imaging filter needs to perform processing after the buffer
* has been allocated but before threads are spawned, the filter can
* can provide an implementation for BeforeThreadedGenerateData().
*
* The execution flow in the default GenerateData() method will be:
* 1) Allocate the output buffer
* 2) Call BeforeThreadedGenerateData()
* 3) Spawn threads, calling ThreadedGenerateData() in each thread.
* 4) Call AfterThreadedGenerateData()
* Note that this flow of control is only available if a filter provides
* a ThreadedGenerateData() method and NOT a GenerateData() method. */
virtual void BeforeThreadedGenerateData() {}
/** @brief If an imaging filter needs to perform processing after all
* processing threads have completed, the filter can can provide an
* implementation for AfterThreadedGenerateData().
*
* The execution
* flow in the default GenerateData() method will be:
* 1) Allocate the output buffer
* 2) Call BeforeThreadedGenerateData()
* 3) Spawn threads, calling ThreadedGenerateData() in each thread.
* 4) Call AfterThreadedGenerateData()
* Note that this flow of control is only available if a filter provides
* a ThreadedGenerateData() method and NOT a GenerateData() method. */
virtual void AfterThreadedGenerateData() {}
/** @brief Split the output's RequestedRegion into "num" pieces, returning
* region "i" as "splitRegion".
*
* This method is called "num" times. The
* regions must not overlap. The method returns the number of pieces that
* the routine is capable of splitting the output RequestedRegion,
* i.e. return value is less than or equal to "num". */
virtual unsigned int SplitRequestedRegion(unsigned int i, unsigned int num, OutputImageRegionType &splitRegion);
/** @brief Static function used as a "callback" by the MultiThreader.
*
* The threading library will call this routine for each thread, which will delegate the
* control to ThreadedGenerateData(). */
- static ITK_THREAD_RETURN_TYPE ThreaderCallback(void *arg);
+ static itk::ITK_THREAD_RETURN_TYPE ThreaderCallback(void *arg);
/** @brief Internal structure used for passing image data into the threading library */
struct ThreadStruct
{
Pointer Filter;
};
private:
ImageSource(const Self &); // purposely not implemented
void operator=(const Self &); // purposely not implemented
};
} // namespace mitk
#endif /* IMAGESOURCE_H_HEADER_INCLUDED_C1E7D6EC */
diff --git a/Modules/Core/include/mitkImageToItk.txx b/Modules/Core/include/mitkImageToItk.txx
index dea853f238..29abe89a77 100644
--- a/Modules/Core/include/mitkImageToItk.txx
+++ b/Modules/Core/include/mitkImageToItk.txx
@@ -1,290 +1,290 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef IMAGETOITK_TXX_INCLUDED_C1C2FCD2
#define IMAGETOITK_TXX_INCLUDED_C1C2FCD2
#include "itkImportMitkImageContainer.h"
#include "mitkBaseProcess.h"
#include "mitkException.h"
#include "mitkImageReadAccessor.h"
#include "mitkImageToItk.h"
#include "mitkImageWriteAccessor.h"
#include <memory>
template <typename TImageType>
struct SetLengthHelper
{
SetLengthHelper(TImageType *in) { m_Image = in; }
private:
TImageType *m_Image;
};
template <typename T, unsigned int VDimension>
struct SetLengthHelper<itk::Image<T, VDimension>>
{
typedef itk::Image<T, VDimension> TImageType;
SetLengthHelper(TImageType *in) { m_Image = in; }
void SetVectorLength(size_t) {}
private:
TImageType *m_Image;
};
template <typename T, unsigned int VDimension>
struct SetLengthHelper<itk::VectorImage<T, VDimension>>
{
typedef itk::VectorImage<T, VDimension> TImageType;
SetLengthHelper(TImageType *in) { m_Image = in; }
void SetVectorLength(size_t len) { m_Image->SetVectorLength(len); }
private:
TImageType *m_Image;
};
template <class TOutputImage>
void mitk::ImageToItk<TOutputImage>::SetInput(mitk::Image *input)
{
this->SetInput(static_cast<const Image *>(input));
m_ConstInput = false;
}
template <class TOutputImage>
void mitk::ImageToItk<TOutputImage>::SetInput(const mitk::Image *input)
{
this->CheckInput(input);
// Process object is not const-correct so the const_cast is required here
itk::ProcessObject::PushFrontInput(input);
m_ConstInput = true;
}
template <class TOutputImage>
mitk::Image *mitk::ImageToItk<TOutputImage>::GetInput(void)
{
if (this->GetNumberOfInputs() < 1)
{
return nullptr;
}
return static_cast<mitk::Image *>(itk::ProcessObject::GetInput(0));
}
template <class TOutputImage>
const mitk::Image *mitk::ImageToItk<TOutputImage>::GetInput() const
{
if (this->GetNumberOfInputs() < 1)
{
return nullptr;
}
return static_cast<const mitk::Image *>(itk::ProcessObject::GetInput(0));
}
template <class TOutputImage>
void mitk::ImageToItk<TOutputImage>::GenerateData()
{
// Allocate output
mitk::Image::Pointer input = this->GetInput();
typename Superclass::OutputImageType::Pointer output = this->GetOutput();
unsigned long noBytes = input->GetDimension(0);
for (unsigned int i = 1; i < TOutputImage::GetImageDimension(); ++i)
{
noBytes = noBytes * input->GetDimension(i);
}
const mitk::PixelType pixelType = input->GetPixelType();
- if (pixelType.GetPixelType() == itk::ImageIOBase::VECTOR)
+ if (pixelType.GetPixelType() == itk::IOPixelEnum::VECTOR)
{
noBytes *= pixelType.GetNumberOfComponents();
SetLengthHelper<typename Superclass::OutputImageType> helper(output.GetPointer());
helper.SetVectorLength(pixelType.GetNumberOfComponents());
}
std::unique_ptr<mitk::ImageAccessorBase> imageAccess;
if (m_ConstInput)
{
imageAccess.reset(new mitk::ImageReadAccessor(input, nullptr, m_Options));
}
else
{
imageAccess.reset(new mitk::ImageWriteAccessor(input, nullptr, m_Options));
}
// hier wird momentan wohl nur der erste Channel verwendet??!!
if (imageAccess->GetData() == nullptr)
{
itkWarningMacro(<< "no image data to import in ITK image");
RegionType bufferedRegion;
output->SetBufferedRegion(bufferedRegion);
return;
}
if (m_CopyMemFlag)
{
itkDebugMacro("copyMem ...");
output->Allocate();
memcpy(output->GetBufferPointer(), imageAccess->GetData(), sizeof(InternalPixelType) * noBytes);
}
else
{
itkDebugMacro("do not copyMem ...");
typedef itk::ImportMitkImageContainer<itk::SizeValueType, InternalPixelType> ImportContainerType;
typename ImportContainerType::Pointer import;
import = ImportContainerType::New();
import->Initialize();
itkDebugMacro(<< "size of container = " << import->Size());
// import->SetImageDataItem(m_ImageDataItem);
import->SetImageAccessor(imageAccess.release(), sizeof(InternalPixelType) * noBytes);
output->SetPixelContainer(import);
itkDebugMacro(<< "size of container = " << import->Size());
}
}
template <class TOutputImage>
void mitk::ImageToItk<TOutputImage>::UpdateOutputInformation()
{
mitk::Image::Pointer input = this->GetInput();
if (input.IsNotNull() && (input->GetSource().IsNotNull()) && input->GetSource()->Updating())
{
typename Superclass::OutputImageType::Pointer output = this->GetOutput();
unsigned long t1 = input->GetUpdateMTime() + 1;
if (t1 > this->m_OutputInformationMTime.GetMTime())
{
output->SetPipelineMTime(t1);
this->GenerateOutputInformation();
this->m_OutputInformationMTime.Modified();
}
return;
}
Superclass::UpdateOutputInformation();
}
template <class TOutputImage>
void mitk::ImageToItk<TOutputImage>::GenerateOutputInformation()
{
mitk::Image::ConstPointer input = this->GetInput();
typename Superclass::OutputImageType::Pointer output = this->GetOutput();
// allocate size, origin, spacing, direction in types of output image
SizeType size;
const unsigned int itkDimMin3 = (TOutputImage::ImageDimension > 3 ? TOutputImage::ImageDimension : 3);
const unsigned int itkDimMax3 = (TOutputImage::ImageDimension < 3 ? TOutputImage::ImageDimension : 3);
typename Superclass::OutputImageType::PointType::ValueType origin[itkDimMin3];
typename Superclass::OutputImageType::SpacingType::ComponentType spacing[itkDimMin3];
typename Superclass::OutputImageType::DirectionType direction;
// copy as much information as possible into size and spacing
unsigned int i;
for (i = 0; i < itkDimMax3; ++i)
{
size[i] = input->GetDimension(i);
spacing[i] = input->GetGeometry()->GetSpacing()[i];
}
for (; i < TOutputImage::ImageDimension; ++i)
{
origin[i] = 0.0;
size[i] = input->GetDimension(i);
spacing[i] = 1.0;
}
// build region from size
IndexType start;
start.Fill(0);
RegionType region;
region.SetIndex(start);
region.SetSize(size);
// copy as much information as possible into origin
const mitk::Point3D &mitkorigin = input->GetGeometry()->GetOrigin();
itk2vtk(mitkorigin, origin);
// copy as much information as possible into direction
direction.SetIdentity();
unsigned int j;
const AffineTransform3D::MatrixType &matrix = input->GetGeometry()->GetIndexToWorldTransform()->GetMatrix();
/// \warning 2D MITK images could have a 3D rotation, since they have a 3x3 geometry matrix.
/// If it is only a rotation around the axial plane normal, it can be express with a 2x2 matrix.
/// In this case, the ITK image conservs this information and is identical to the MITK image!
/// If the MITK image contains any other rotation, the ITK image will have no rotation at all.
/// Spacing is of course conserved in both cases.
// the following loop devides by spacing now to normalize columns.
// counterpart of InitializeByItk in mitkImage.h line 372 of revision 15092.
// Check if information is lost
if (TOutputImage::ImageDimension <= 2)
{
if ((TOutputImage::ImageDimension == 2) && ((matrix[0][2] != 0) || (matrix[1][2] != 0) || (matrix[2][0] != 0) ||
(matrix[2][1] != 0) || ((matrix[2][2] != 1) && (matrix[2][2] != -1))))
{
// The 2D MITK image contains 3D rotation information.
// This cannot be expressed in a 2D ITK image, so the ITK image will have no rotation
}
else
{
// The 2D MITK image can be converted to an 2D ITK image without information loss!
for (i = 0; i < itkDimMax3; ++i)
for (j = 0; j < itkDimMax3; ++j)
direction[i][j] = matrix[i][j] / spacing[j];
}
}
else
{
// Normal 3D image. Conversion possible without problem!
for (i = 0; i < itkDimMax3; ++i)
for (j = 0; j < itkDimMax3; ++j)
direction[i][j] = matrix[i][j] / spacing[j];
}
// set information into output image
output->SetRegions(region);
output->SetOrigin(origin);
output->SetSpacing(spacing);
output->SetDirection(direction);
}
template <class TOutputImage>
void mitk::ImageToItk<TOutputImage>::CheckInput(const mitk::Image *input) const
{
if (input == nullptr)
{
itkExceptionMacro(<< "image is null");
}
if (input->GetDimension() != TOutputImage::GetImageDimension())
{
itkExceptionMacro(<< "image has dimension " << input->GetDimension() << " instead of "
<< TOutputImage::GetImageDimension());
}
if (!(input->GetPixelType() == mitk::MakePixelType<TOutputImage>(input->GetPixelType().GetNumberOfComponents())))
{
itkExceptionMacro(<< "image has wrong pixel type ");
}
}
template <class TOutputImage>
void mitk::ImageToItk<TOutputImage>::PrintSelf(std::ostream &os, itk::Indent indent) const
{
Superclass::PrintSelf(os, indent);
}
#endif // IMAGETOITK_TXX_INCLUDED_C1C2FCD2
diff --git a/Modules/Core/include/mitkInteractionSchemeSwitcher.h b/Modules/Core/include/mitkInteractionSchemeSwitcher.h
index 139d6f0738..bd4aa4105c 100644
--- a/Modules/Core/include/mitkInteractionSchemeSwitcher.h
+++ b/Modules/Core/include/mitkInteractionSchemeSwitcher.h
@@ -1,116 +1,116 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKINTERACTIONSCHEMESWITCHER_H
#define MITKINTERACTIONSCHEMESWITCHER_H
#include "MitkCoreExports.h"
#include "mitkInteractionEventHandler.h"
#include <itkObject.h>
namespace mitk
{
+#pragma GCC visibility push(default)
+ /**
+ \brief Can be observed by GUI class to update button states when type is changed programmatically.
+ */
+ itkEventMacroDeclaration(InteractionSchemeChangedEvent, itk::AnyEvent);
+#pragma GCC visibility pop
+
/***********************************************************************
*
* \brief Class that offers a convenient way to switch between different
* interaction schemes.
*
* This class offers the possibility to switch between the different
* interaction schemes that are available:
*
* - MITKStandard : The original MITK interaction scheme
* - MITKRotationUncoupled : A modified MITK interaction scheme with rotation
* - MITKRotationCoupled : A modified MTIK interaction scheme with coupled rotation
* - MITKSwivel : A modified MITK interaction scheme with plane swiveling
*
* - PACS : An alternative interaction scheme that behaves more like a
* PACS workstation
* - left mouse button : behavior depends on current PACS scheme
* Always enabled:
* - middle mouse button : fast scrolling
* - right mouse button : level-window
* - ctrl + right button : zooming
* - shift+ right button : panning
*
* There are 6 different PACS schemes.
* Each scheme defines the interaction that is performed on a left
* mouse button click:
* - PACSBase : No interaction on a left mouse button click
- This scheme serves as a base for other PACS schemes and defines the right
and middle mouse button clicks, which are available in every PACS scheme.
* - PACSStandard : Sets the cross position for the MPR
* - PACSLevelWindow : Sets the level window
* - PACSPan : Moves the slice
* - PACSScroll : Scrolls through the slices stepwise
* - PACSZoom : Zooms into / out of the slice
*
* When the interaction scheme is changed, this class sets the corresponding
* interaction .xml-files for a given interaction event handler.
*
***********************************************************************/
class MITKCORE_EXPORT InteractionSchemeSwitcher : public itk::Object
{
public:
-#pragma GCC visibility push(default)
- /**
- \brief Can be observed by GUI class to update button states when type is changed programmatically.
- */
- itkEventMacro(InteractionSchemeChangedEvent, itk::AnyEvent);
-#pragma GCC visibility pop
-
mitkClassMacroItkParent(InteractionSchemeSwitcher, itk::Object);
itkFactorylessNewMacro(Self);
// enum of the different interaction schemes that are available
enum InteractionScheme
{
MITKStandard = 0,
MITKRotationUncoupled,
MITKRotationCoupled,
MITKSwivel,
PACSBase,
PACSStandard,
PACSLevelWindow,
PACSPan,
PACSScroll,
PACSZoom
};
/**
* @brief Set the current interaction scheme of the given interaction event handler
*
* The interaction event handler is able to accept xml-configuration files that will define the interaction scheme.
* Based on the given interaction scheme different configuration files are loaded into the interaction event handler.
* The interaction scheme can be a variant of the MITK-scheme or the PACS-scheme (see 'enum InteractionScheme').
* The default is 'MITKStandard'.
* If the interaction scheme has been changed, an 'InteractionSchemeChangedEvent' will be invoked.
*
* @pre The interaction event handler has to be valid (!nullptr).
* @throw mitk::Exception, if the interaction event handler is invalid (==nullptr).
*
* @param interactionEventHandler The interaction event handler that defines the interaction scheme via configuration files
* @param interactionScheme The interaction scheme that should be used for the currently active interaction event handler.
*/
void SetInteractionScheme(mitk::InteractionEventHandler* interactionEventHandler, InteractionScheme interactionScheme);
protected:
InteractionSchemeSwitcher();
~InteractionSchemeSwitcher() override;
};
} // namespace mitk
#endif // MITKINTERACTIONSCHEMESWITCHER_H
diff --git a/Modules/Core/include/mitkLimitedLinearUndo.h b/Modules/Core/include/mitkLimitedLinearUndo.h
index fee98b5b91..3d724b1db7 100644
--- a/Modules/Core/include/mitkLimitedLinearUndo.h
+++ b/Modules/Core/include/mitkLimitedLinearUndo.h
@@ -1,159 +1,159 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef LIMITEDLINEARUNDO_H_HEADER_INCLUDED_C16E9C96
#define LIMITEDLINEARUNDO_H_HEADER_INCLUDED_C16E9C96
// MITK header
#include "mitkOperationEvent.h"
#include "mitkUndoModel.h"
#include <MitkCoreExports.h>
// STL header
#include <vector>
// ITK header
#pragma GCC visibility push(default)
#include <itkEventObject.h>
#pragma GCC visibility pop
#include <deque>
namespace mitk
{
//##Documentation
//## @brief A linear undo model with one undo and one redo stack.
//##
//## Derived from UndoModel AND itk::Object. Invokes ITK-events to signal listening
//## GUI elements, whether each of the stacks is empty or not (to enable/disable button, ...)
class MITKCORE_EXPORT LimitedLinearUndo : public UndoModel
{
public:
typedef std::deque<UndoStackItem *> UndoContainer;
typedef std::deque<UndoStackItem *>::reverse_iterator UndoContainerRevIter;
mitkClassMacro(LimitedLinearUndo, UndoModel);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
bool SetOperationEvent(UndoStackItem *stackItem) override;
//##Documentation
//## @brief Undoes the last changes
//##
//## Reads the top element of the Undo-Stack,
//## executes the operation,
//## swaps the OperationEvent-Undo with the Operation
//## and sets it to Redo-Stack
bool Undo() override;
bool Undo(bool) override;
//##Documentation
//## @brief Undoes all changes until ObjectEventID oeid
virtual bool Undo(int oeid);
//##Documentation
//## @brief Undoes the last changes
//##
//## Reads the top element of the Redo-Stack,
//## executes the operation,
//## swaps the OperationEvent-Operation with the Undo-Operation
//## and sets it to Undo-Stack
bool Redo() override;
bool Redo(bool) override;
//##Documentation
//## @brief Redoes all changes until ObjectEventID oeid
virtual bool Redo(int oeid);
//##Documentation
//## @brief Clears UndoList and RedoList
void Clear() override;
//##Documentation
//## @brief Clears the RedoList
void ClearRedoList() override;
//##Documentation
//## @brief True, if RedoList is empty
bool RedoListEmpty() override;
//##Documentation
//## @brief Gets the limit on the size of the undo history.
//## The undo limit determines how many items can be stored
//## in the undo stack. If the value is 0 that means that
//## there is no limit.
std::size_t GetUndoLimit() const override;
//##Documentation
//## @brief Sets a limit on the size of the undo history.
//## If the limit is reached, the oldest undo items will
//## be dropped from the bottom of the undo stack.
//## The 0 value means that there is no limit.
//## @param limit the maximum number of items on the stack
void SetUndoLimit(std::size_t limit) override;
//##Documentation
//## @brief Returns the ObjectEventId of the
//## top element in the OperationHistory
int GetLastObjectEventIdInList() override;
//##Documentation
//## @brief Returns the GroupEventId of the
//## top element in the OperationHistory
int GetLastGroupEventIdInList() override;
//##Documentation
//## @brief Returns the last specified OperationEvent in Undo-list
//## corresponding to the given values; if nothing found, then returns nullptr
OperationEvent *GetLastOfType(OperationActor *destination, OperationType opType) override;
protected:
//##Documentation
//## Constructor
LimitedLinearUndo();
//##Documentation
//## Destructor
~LimitedLinearUndo() override;
//## @brief Convenience method to free the memory of
//## elements in the list and to clear the list
void ClearList(UndoContainer *list);
UndoContainer m_UndoList;
UndoContainer m_RedoList;
private:
int FirstObjectEventIdOfCurrentGroup(UndoContainer &stack);
std::size_t m_UndoLimit;
};
#pragma GCC visibility push(default)
/// Some itk events to notify listening GUI elements, when the undo or redo stack is empty (diable undo button)
/// or when there are items in the stack (enable button)
- itkEventMacro(UndoStackEvent, itk::ModifiedEvent);
- itkEventMacro(UndoEmptyEvent, UndoStackEvent);
- itkEventMacro(RedoEmptyEvent, UndoStackEvent);
- itkEventMacro(UndoNotEmptyEvent, UndoStackEvent);
- itkEventMacro(RedoNotEmptyEvent, UndoStackEvent);
+ itkEventMacroDeclaration(UndoStackEvent, itk::ModifiedEvent);
+ itkEventMacroDeclaration(UndoEmptyEvent, UndoStackEvent);
+ itkEventMacroDeclaration(RedoEmptyEvent, UndoStackEvent);
+ itkEventMacroDeclaration(UndoNotEmptyEvent, UndoStackEvent);
+ itkEventMacroDeclaration(RedoNotEmptyEvent, UndoStackEvent);
/// Additional unused events, if anybody wants to put an artificial limit to the possible number of items in the stack
- itkEventMacro(UndoFullEvent, UndoStackEvent);
- itkEventMacro(RedoFullEvent, UndoStackEvent);
+ itkEventMacroDeclaration(UndoFullEvent, UndoStackEvent);
+ itkEventMacroDeclaration(RedoFullEvent, UndoStackEvent);
#pragma GCC visibility pop
} // namespace mitk
#endif /* LIMITEDLINEARUNDO_H_HEADER_INCLUDED_C16E9C96 */
diff --git a/Modules/Core/include/mitkLocalStorageHandler.h b/Modules/Core/include/mitkLocalStorageHandler.h
index 5089b5a39f..86349b4138 100644
--- a/Modules/Core/include/mitkLocalStorageHandler.h
+++ b/Modules/Core/include/mitkLocalStorageHandler.h
@@ -1,115 +1,112 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef LOCALSTORAGEHANDLER_H_HEADER_INCLUDED_C1E6EA08
#define LOCALSTORAGEHANDLER_H_HEADER_INCLUDED_C1E6EA08
#include "mitkBaseRenderer.h"
#include "mitkCommon.h"
#include "mitkLevelWindow.h"
#include "mitkVtkPropRenderer.h"
#include <MitkCoreExports.h>
#include <itkObject.h>
#include <itkWeakPointer.h>
-// Just included to get VTK version
-#include <vtkConfigure.h>
-
class vtkWindow;
class vtkProp;
namespace mitk
{
/** \brief Interface for accessing (templated) LocalStorageHandler instances.
*/
class BaseLocalStorageHandler
{
public:
virtual ~BaseLocalStorageHandler() {}
virtual void ClearLocalStorage(mitk::BaseRenderer *renderer, bool unregisterFromBaseRenderer = true) = 0;
};
/** \brief Templated class for management of LocalStorage implementations in Mappers.
*
* The LocalStorageHandler is responsible for providing a LocalStorage to a
* concrete mitk::Mapper subclass. Each RenderWindow / mitk::BaseRenderer is
* assigned its own LocalStorage instance so that all contained ressources
* (actors, shaders, textures, ...) are provided individually per window.
*
*/
template <class L>
class LocalStorageHandler : public mitk::BaseLocalStorageHandler
{
protected:
std::map<mitk::BaseRenderer *, L *> m_BaseRenderer2LS;
public:
/** \brief deallocates a local storage for a specifc BaseRenderer (if the
* BaseRenderer is itself deallocating it in its destructor, it has to set
* unregisterFromBaseRenderer=false)
*/
void ClearLocalStorage(mitk::BaseRenderer *renderer, bool unregisterFromBaseRenderer = true) override
{
// MITK_INFO << "deleting a localstorage on a mapper request";
if (unregisterFromBaseRenderer)
renderer->UnregisterLocalStorageHandler(this);
L *l = m_BaseRenderer2LS[renderer];
m_BaseRenderer2LS.erase(renderer);
delete l;
}
std::vector<mitk::BaseRenderer *> GetRegisteredBaseRenderer()
{
std::vector<mitk::BaseRenderer *> baserenderers;
typename std::map<mitk::BaseRenderer *, L *>::iterator it;
for (it = m_BaseRenderer2LS.begin(); it != m_BaseRenderer2LS.end(); ++it)
{
baserenderers.push_back(it->first);
}
return baserenderers;
}
/** \brief Retrieves a LocalStorage for a specific BaseRenderer.
*
* Should be used by mappers in GenerateDataForRenderer()
*/
L *GetLocalStorage(mitk::BaseRenderer *forRenderer)
{
L *l = m_BaseRenderer2LS[forRenderer];
if (!l)
{
// MITK_INFO << "creating new localstorage";
l = new L;
m_BaseRenderer2LS[forRenderer] = l;
forRenderer->RegisterLocalStorageHandler(this);
}
return l;
}
~LocalStorageHandler() override
{
typename std::map<mitk::BaseRenderer *, L *>::iterator it;
for (it = m_BaseRenderer2LS.begin(); it != m_BaseRenderer2LS.end(); it++)
{
(*it).first->UnregisterLocalStorageHandler(this);
delete (*it).second;
}
m_BaseRenderer2LS.clear();
}
};
} // namespace mitk
#endif /* LOCALSTORAGEHANDLER_H_HEADER_INCLUDED_C1E6EA08 */
diff --git a/Modules/Core/include/mitkMapper.h b/Modules/Core/include/mitkMapper.h
index b3f7e44ecd..c30e6411e0 100644
--- a/Modules/Core/include/mitkMapper.h
+++ b/Modules/Core/include/mitkMapper.h
@@ -1,214 +1,211 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MAPPER_H_HEADER_INCLUDED_C1E6EA08
#define MAPPER_H_HEADER_INCLUDED_C1E6EA08
#include "mitkBaseRenderer.h"
#include "mitkCommon.h"
#include "mitkLevelWindow.h"
#include "mitkLocalStorageHandler.h"
#include "mitkVtkPropRenderer.h"
#include <MitkCoreExports.h>
#include <itkObject.h>
#include <itkWeakPointer.h>
-// Just included to get VTK version
-#include <vtkConfigure.h>
-
class vtkWindow;
class vtkProp;
namespace mitk
{
class BaseRenderer;
class BaseData;
class DataNode;
/** \brief Base class of all mappers, Vtk as well as OpenGL mappers
*
* By the help of mappers, the input data is transformed to tangible primitives,
* such as surfaces, points, lines, etc.
* This is the base class of all mappers, Vtk as well as OpenGL mappers.
* Subclasses of mitk::Mapper control the creation of rendering primitives
* that interface to the graphics library (e.g., OpenGL, vtk).
*
* \todo Should Mapper be a subclass of ImageSource?
* \ingroup Mapper
*/
class MITKCORE_EXPORT Mapper : public itk::Object
{
public:
mitkClassMacroItkParent(Mapper, itk::Object);
/** \brief Set the DataNode containing the data to map */
itkSetObjectMacro(DataNode, DataNode);
/** \brief Get the DataNode containing the data to map.
* Method only returns valid DataNode Pointer if the mapper belongs to a data node.
* Otherwise, the returned DataNode Pointer might be invalid. */
virtual DataNode *GetDataNode() const;
/**\brief Get the data to map
*
* Returns the mitk::BaseData object associated with this mapper.
* \return the mitk::BaseData associated with this mapper.
* \deprecatedSince{2013_03} Use GetDataNode()->GetData() instead to access the data
*/
DEPRECATED(BaseData *GetData() const);
/** \brief Convenience access method for color properties (instances of
* ColorProperty)
* \return \a true property was found
* \deprecatedSince{2013_03} Use GetDataNode()->GetColor(...) instead to get the color
*/
DEPRECATED(virtual bool GetColor(float rgb[3], BaseRenderer *renderer, const char *name = "color") const);
/** \brief Convenience access method for visibility properties (instances
* of BoolProperty)
* \return \a true property was found
* \sa IsVisible
* \deprecatedSince{2013_03} Use GetDataNode()->GetVisibility(...) instead to get the visibility
*/
DEPRECATED(virtual bool GetVisibility(bool &visible, BaseRenderer *renderer, const char *name = "visible") const);
/** \brief Convenience access method for opacity properties (instances of
* FloatProperty)
* \return \a true property was found
* \deprecatedSince{2013_03} Use GetDataNode()->GetOpacity(...) instead to get the opacity
*/
DEPRECATED(virtual bool GetOpacity(float &opacity, BaseRenderer *renderer, const char *name = "opacity") const);
/** \brief Convenience access method for color properties (instances of
* LevelWindoProperty)
* \return \a true property was found
* \deprecatedSince{2013_03} Use GetDataNode->GetLevelWindow(...) instead to get the levelwindow
*/
DEPRECATED(virtual bool GetLevelWindow(LevelWindow &levelWindow,
BaseRenderer *renderer,
const char *name = "levelwindow") const);
/** \brief Convenience access method for visibility properties (instances
* of BoolProperty). Return value is the visibility. Default is
* visible==true, i.e., true is returned even if the property (\a
* propertyKey) is not found.
*
* Thus, the return value has a different meaning than in the
* GetVisibility method!
* \sa GetVisibility
* \deprecatedSince{2013_03} Use GetDataNode()->GetVisibility(...) instead
*/
DEPRECATED(virtual bool IsVisible(BaseRenderer *renderer, const char *name = "visible") const);
/** \brief Returns whether this is an vtk-based mapper
* \deprecatedSince{2013_03} All mappers of superclass VTKMapper are vtk based, use a dynamic_cast instead
*/
virtual bool IsVtkBased() const { return true; }
/** \brief Calls the time step of the input data for the specified renderer and checks
* whether the time step is valid and calls method GenerateDataForRenderer()
*/
virtual void Update(BaseRenderer *renderer);
/** \brief Responsible for calling the appropriate render functions.
* To be implemented in sub-classes.
*/
virtual void MitkRender(mitk::BaseRenderer *renderer, mitk::VtkPropRenderer::RenderType type) = 0;
/**
* \brief Apply specific color and opacity properties read from the PropertyList.
* Reimplemented in GLmapper (does not use the actor) and the VtkMapper class.
* The function is called by the individual mapper (mostly in the ApplyProperties() or ApplyAllProperties()
* method).
*/
virtual void ApplyColorAndOpacityProperties(mitk::BaseRenderer *renderer, vtkActor *actor = nullptr) = 0;
/** \brief Set default values of properties used by this mapper
* to \a node
*
* \param node The node for which the properties are set
* \param overwrite overwrite existing properties (default: \a false)
* \param renderer defines which property list of node is used
* (default: \a nullptr, i.e. default property list)
*/
static void SetDefaultProperties(DataNode *node, BaseRenderer *renderer = nullptr, bool overwrite = false);
/** \brief Returns the current time step as calculated from the renderer */
int GetTimestep() const { return m_TimeStep; }
/** Returns true if this Mapper currently allows for Level-of-Detail rendering.
* This reflects whether this Mapper currently invokes StartEvent, EndEvent, and
* ProgressEvent on BaseRenderer. */
virtual bool IsLODEnabled(BaseRenderer * /*renderer*/) const { return false; }
protected:
/** \brief explicit constructor which disallows implicit conversions */
explicit Mapper();
/** \brief virtual destructor in order to derive from this class */
~Mapper() override;
/** \brief Generate the data needed for rendering (independent of a specific renderer)
* \deprecatedSince{2013_03} Use GenerateDataForRenderer(BaseRenderer* renderer) instead.
*/
DEPRECATED(virtual void GenerateData()) {}
/** \brief Generate the data needed for rendering into \a renderer */
virtual void GenerateDataForRenderer(BaseRenderer * /* renderer */) {}
/** \brief Updates the time step, which is sometimes needed in subclasses */
virtual void CalculateTimeStep(BaseRenderer *renderer);
/** \brief Reset the mapper (i.e., make sure that nothing is displayed) if no
* valid data is present. In most cases the reimplemented function
* disables the according actors (toggling visibility off)
*
* To be implemented in sub-classes.
*/
virtual void ResetMapper(BaseRenderer * /*renderer*/) {}
mitk::DataNode *m_DataNode;
private:
/** \brief The current time step of the dataset to be rendered,
* for use in subclasses.
* The current timestep can be accessed via the GetTimestep() method.
*/
int m_TimeStep;
/** \brief copy constructor */
Mapper(const Mapper &);
/** \brief assignment operator */
Mapper &operator=(const Mapper &);
public:
/** \brief Base class for mapper specific rendering ressources.
*/
class MITKCORE_EXPORT BaseLocalStorage
{
public:
BaseLocalStorage() = default;
virtual ~BaseLocalStorage() = default;
BaseLocalStorage(const BaseLocalStorage &) = delete;
BaseLocalStorage & operator=(const BaseLocalStorage &) = delete;
bool IsGenerateDataRequired(mitk::BaseRenderer *renderer, mitk::Mapper *mapper, mitk::DataNode *dataNode) const;
inline void UpdateGenerateDataTime() { m_LastGenerateDataTime.Modified(); }
inline itk::TimeStamp &GetLastGenerateDataTime() { return m_LastGenerateDataTime; }
protected:
/** \brief timestamp of last update of stored data */
itk::TimeStamp m_LastGenerateDataTime;
};
};
} // namespace mitk
#endif /* MAPPER_H_HEADER_INCLUDED_C1E6EA08 */
diff --git a/Modules/Core/include/mitkMessage.h b/Modules/Core/include/mitkMessage.h
index 47cf00cac8..564472316c 100644
--- a/Modules/Core/include/mitkMessage.h
+++ b/Modules/Core/include/mitkMessage.h
@@ -1,697 +1,697 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkMessageHIncluded
#define mitkMessageHIncluded
#include <functional>
-#include <itkSimpleFastMutexLock.h>
+#include <mutex>
#include <vector>
/**
* Adds a Message<> variable and methods to add/remove message delegates to/from
* this variable.
*/
#define mitkNewMessageMacro(msgHandleObject) \
private: \
::mitk::Message<> m_##msgHandleObject##Message; \
\
public: \
inline void Add##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate<> &delegate) \
{ \
m_##msgHandleObject##Message += delegate; \
} \
inline void Remove##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate<> &delegate) \
{ \
m_##msgHandleObject##Message -= delegate; \
}
#define mitkNewMessageWithReturnMacro(msgHandleObject, returnType) \
private: \
::mitk::Message<returnType> m_##msgHandleObject##Message; \
\
public: \
inline void Add##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate<returnType> &delegate) \
{ \
m_##msgHandleObject##Message += delegate; \
} \
inline void Remove##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate<returnType> &delegate) \
{ \
m_##msgHandleObject##Message -= delegate; \
}
#define mitkNewMessage1Macro(msgHandleObject, type1) \
private: \
::mitk::Message1<type1> m_##msgHandleObject##Message; \
\
public: \
void Add##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate1<type1> &delegate) \
{ \
m_##msgHandleObject##Message += delegate; \
} \
void Remove##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate1<type1> &delegate) \
{ \
m_##msgHandleObject##Message -= delegate; \
}
#define mitkNewMessage2Macro(msgHandleObject, type1, type2) \
private: \
::mitk::Message2<type1, type2> m_##msgHandleObject##Message; \
\
public: \
void Add##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate2<type1, type2> &delegate) \
{ \
m_##msgHandleObject##Message += delegate; \
} \
void Remove##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate2<type1, type2> &delegate) \
{ \
m_##msgHandleObject##Message -= delegate; \
}
namespace mitk
{
template <typename A = void>
class MessageAbstractDelegate
{
public:
virtual ~MessageAbstractDelegate() {}
virtual A Execute() const = 0;
virtual bool operator==(const MessageAbstractDelegate *cmd) const = 0;
virtual MessageAbstractDelegate *Clone() const = 0;
};
template <typename T, typename A = void>
class MessageAbstractDelegate1
{
public:
virtual ~MessageAbstractDelegate1() {}
virtual A Execute(T t) const = 0;
virtual bool operator==(const MessageAbstractDelegate1 *cmd) const = 0;
virtual MessageAbstractDelegate1 *Clone() const = 0;
};
template <typename T, typename U, typename A = void>
class MessageAbstractDelegate2
{
public:
virtual ~MessageAbstractDelegate2() {}
virtual A Execute(T t, U u) const = 0;
virtual bool operator==(const MessageAbstractDelegate2 *cmd) const = 0;
virtual MessageAbstractDelegate2 *Clone() const = 0;
};
template <typename T, typename U, typename V, typename A = void>
class MessageAbstractDelegate3
{
public:
virtual ~MessageAbstractDelegate3() {}
virtual A Execute(T t, U u, V v) const = 0;
virtual bool operator==(const MessageAbstractDelegate3 *cmd) const = 0;
virtual MessageAbstractDelegate3 *Clone() const = 0;
};
template <typename T, typename U, typename V, typename W, typename A = void>
class MessageAbstractDelegate4
{
public:
virtual ~MessageAbstractDelegate4() {}
virtual A Execute(T t, U u, V v, W w) const = 0;
virtual bool operator==(const MessageAbstractDelegate4 *cmd) const = 0;
virtual MessageAbstractDelegate4 *Clone() const = 0;
};
/**
* This class essentially wraps a function pointer with signature
* A(R::*function)(). A is the return type of your callback function
* and R the type of the class implementing the function.
*
* Use this class to add a callback function to
* messages without parameters.
*/
template <class R, typename A = void>
class MessageDelegate : public MessageAbstractDelegate<A>
{
public:
// constructor - takes pointer to an object and pointer to a member and stores
// them in two private variables
MessageDelegate(R *object, A (R::*memberFunctionPointer)())
: m_Object(object), m_MemberFunctionPointer(memberFunctionPointer)
{
}
~MessageDelegate() override {}
// override function "Call"
A Execute() const override
{
return (m_Object->*m_MemberFunctionPointer)(); // execute member function
}
bool operator==(const MessageAbstractDelegate<A> *c) const override
{
const MessageDelegate<R, A> *cmd = dynamic_cast<const MessageDelegate<R, A> *>(c);
if (!cmd)
return false;
if ((void *)this->m_Object != (void *)cmd->m_Object)
return false;
if (this->m_MemberFunctionPointer != cmd->m_MemberFunctionPointer)
return false;
return true;
}
MessageAbstractDelegate<A> *Clone() const override { return new MessageDelegate(m_Object, m_MemberFunctionPointer); }
private:
R *m_Object; // pointer to object
A (R::*m_MemberFunctionPointer)(); // pointer to member function
};
/**
* This class essentially wraps a function pointer with signature
* A(R::*function)(T). A is the return type of your callback function,
* R the type of the class implementing the function and T the type
* of the argument.
*
* Use this class to add a callback function to
* messages with one parameter.
*
* If you need more parameters, use MessageDelegate2 etc.
*/
template <class R, typename T, typename A = void>
class MessageDelegate1 : public MessageAbstractDelegate1<T, A>
{
public:
// constructor - takes pointer to an object and pointer to a member and stores
// them in two private variables
MessageDelegate1(R *object, A (R::*memberFunctionPointer)(T))
: m_Object(object), m_MemberFunctionPointer(memberFunctionPointer)
{
}
~MessageDelegate1() override {}
// override function "Call"
A Execute(T t) const override
{
return (m_Object->*m_MemberFunctionPointer)(t); // execute member function
}
bool operator==(const MessageAbstractDelegate1<T, A> *c) const override
{
const MessageDelegate1<R, T, A> *cmd = dynamic_cast<const MessageDelegate1<R, T, A> *>(c);
if (!cmd)
return false;
if ((void *)this->m_Object != (void *)cmd->m_Object)
return false;
if (this->m_MemberFunctionPointer != cmd->m_MemberFunctionPointer)
return false;
return true;
}
MessageAbstractDelegate1<T, A> *Clone() const override { return new MessageDelegate1(m_Object, m_MemberFunctionPointer); }
private:
R *m_Object; // pointer to object
A (R::*m_MemberFunctionPointer)(T); // pointer to member function
};
template <class R, typename T, typename U, typename A = void>
class MessageDelegate2 : public MessageAbstractDelegate2<T, U, A>
{
public:
// constructor - takes pointer to an object and pointer to a member and stores
// them in two private variables
MessageDelegate2(R *object, A (R::*memberFunctionPointer)(T, U))
: m_Object(object), m_MemberFunctionPointer(memberFunctionPointer)
{
}
~MessageDelegate2() override {}
// override function "Call"
A Execute(T t, U u) const override
{
return (m_Object->*m_MemberFunctionPointer)(t, u); // execute member function
}
bool operator==(const MessageAbstractDelegate2<T, U, A> *c) const override
{
const MessageDelegate2<R, T, U, A> *cmd = dynamic_cast<const MessageDelegate2<R, T, U, A> *>(c);
if (!cmd)
return false;
if ((void *)this->m_Object != (void *)cmd->m_Object)
return false;
if (this->m_MemberFunctionPointer != cmd->m_MemberFunctionPointer)
return false;
return true;
}
MessageAbstractDelegate2<T, U, A> *Clone() const override { return new MessageDelegate2(m_Object, m_MemberFunctionPointer); }
private:
R *m_Object; // pointer to object
A (R::*m_MemberFunctionPointer)(T, U); // pointer to member function
};
template <class R, typename T, typename U, typename V, typename A = void>
class MessageDelegate3 : public MessageAbstractDelegate3<T, U, V, A>
{
public:
// constructor - takes pointer to an object and pointer to a member and stores
// them in two private variables
MessageDelegate3(R *object, A (R::*memberFunctionPointer)(T, U, V))
: m_Object(object), m_MemberFunctionPointer(memberFunctionPointer)
{
}
~MessageDelegate3() override {}
// override function "Call"
A Execute(T t, U u, V v) const override
{
return (m_Object->*m_MemberFunctionPointer)(t, u, v); // execute member function
}
bool operator==(const MessageAbstractDelegate3<T, U, V, A> *c) const override
{
const MessageDelegate3<R, T, U, V, A> *cmd = dynamic_cast<const MessageDelegate3<R, T, U, V, A> *>(c);
if (!cmd)
return false;
if ((void *)this->m_Object != (void *)cmd->m_Object)
return false;
if (this->m_MemberFunctionPointer != cmd->m_MemberFunctionPointer)
return false;
return true;
}
MessageAbstractDelegate3<T, U, V, A> *Clone() const override
{
return new MessageDelegate3(m_Object, m_MemberFunctionPointer);
}
private:
R *m_Object; // pointer to object
A (R::*m_MemberFunctionPointer)(T, U, V); // pointer to member function
};
template <class R, typename T, typename U, typename V, typename W, typename A = void>
class MessageDelegate4 : public MessageAbstractDelegate4<T, U, V, W, A>
{
public:
// constructor - takes pointer to an object and pointer to a member and stores
// them in two private variables
MessageDelegate4(R *object, A (R::*memberFunctionPointer)(T, U, V, W))
: m_Object(object), m_MemberFunctionPointer(memberFunctionPointer)
{
}
virtual ~MessageDelegate4() {}
// override function "Call"
virtual A Execute(T t, U u, V v, W w) const
{
return (m_Object->*m_MemberFunctionPointer)(t, u, v, w); // execute member function
}
bool operator==(const MessageAbstractDelegate4<T, U, V, W, A> *c) const
{
const MessageDelegate4<R, T, U, V, W, A> *cmd = dynamic_cast<const MessageDelegate4<R, T, U, V, W, A> *>(c);
if (!cmd)
return false;
if ((void *)this->m_Object != (void *)cmd->m_Object)
return false;
if (this->m_MemberFunctionPointer != cmd->m_MemberFunctionPointer)
return false;
return true;
}
MessageAbstractDelegate4<T, U, V, W, A> *Clone() const
{
return new MessageDelegate4(m_Object, m_MemberFunctionPointer);
}
private:
R *m_Object; // pointer to object
A (R::*m_MemberFunctionPointer)(T, U, V, W); // pointer to member function
};
template <typename AbstractDelegate>
class MessageBase
{
public:
typedef std::vector<AbstractDelegate *> ListenerList;
virtual ~MessageBase()
{
for (auto iter = m_Listeners.begin(); iter != m_Listeners.end(); ++iter)
{
delete *iter;
}
}
MessageBase() {}
MessageBase(const MessageBase &o)
{
for (typename ListenerList::iterator iter = o.m_Listeners.begin(); iter != o.m_Listeners.end(); ++iter)
{
m_Listeners.push_back((*iter)->Clone());
}
}
MessageBase &operator=(const MessageBase &o)
{
MessageBase tmp(o);
std::swap(tmp.m_Listeners, this->m_Listeners);
return *this;
}
void AddListener(const AbstractDelegate &delegate) const
{
AbstractDelegate *msgCmd = delegate.Clone();
- m_Mutex.Lock();
+ m_Mutex.lock();
for (auto iter = m_Listeners.begin(); iter != m_Listeners.end(); ++iter)
{
if ((*iter)->operator==(msgCmd))
{
delete msgCmd;
- m_Mutex.Unlock();
+ m_Mutex.unlock();
return;
}
}
m_Listeners.push_back(msgCmd);
- m_Mutex.Unlock();
+ m_Mutex.unlock();
}
void operator+=(const AbstractDelegate &delegate) const { this->AddListener(delegate); }
void RemoveListener(const AbstractDelegate &delegate) const
{
- m_Mutex.Lock();
+ m_Mutex.lock();
for (auto iter = m_Listeners.begin(); iter != m_Listeners.end(); ++iter)
{
if ((*iter)->operator==(&delegate))
{
delete *iter;
m_Listeners.erase(iter);
- m_Mutex.Unlock();
+ m_Mutex.unlock();
return;
}
}
- m_Mutex.Unlock();
+ m_Mutex.unlock();
}
void operator-=(const AbstractDelegate &delegate) const { this->RemoveListener(delegate); }
const ListenerList &GetListeners() const { return m_Listeners; }
bool HasListeners() const { return !m_Listeners.empty(); }
bool IsEmpty() const { return m_Listeners.empty(); }
protected:
/**
* \brief List of listeners.
*
* This is declared mutable for a reason: Imagine an object that sends out notifications and
* someone gets a <tt>const Database</tt> object, because he/she should not write to the
* database. He/she should anyway be able to register for notifications about changes in the database
* -- this is why AddListener and RemoveListener are declared <tt>const</tt>. m_Listeners must be
* mutable so that AddListener and RemoveListener can modify it regardless of the object's constness.
*/
mutable ListenerList m_Listeners;
- mutable itk::SimpleFastMutexLock m_Mutex;
+ mutable std::mutex m_Mutex;
};
/**
* \brief Event/message/notification class.
*
* \sa mitk::BinaryThresholdTool
* \sa QmitkBinaryThresholdToolGUI
*
* This totally ITK, Qt, VTK, whatever toolkit independent class
* allows one class to send out messages and another class to
* receive these message. This class is templated over the
* return type (A) of the callback functions.
* There are variations of this class
* (Message1, Message2, etc.) for sending
* one, two or more parameters along with the messages.
*
* This is an implementation of the Observer pattern.
*
* \li There is no guarantee about the order of which observer is notified first. At the moment the observers which
* register first will be notified first.
* \li Notifications are <b>synchronous</b>, by direct method calls. There is no support for asynchronous messages.
*
* To conveniently add methods for registering/unregistering observers
* to Message variables of your class, you can use the mitkNewMessageMacro
* macros.
*/
template <typename A = void>
class Message : public MessageBase<MessageAbstractDelegate<A>>
{
public:
typedef MessageBase<MessageAbstractDelegate<A>> Super;
typedef typename Super::ListenerList ListenerList;
void Send() const
{
ListenerList listeners;
{
- this->m_Mutex.Lock();
+ this->m_Mutex.lock();
listeners.assign(this->m_Listeners.begin(), this->m_Listeners.end());
- this->m_Mutex.Unlock();
+ this->m_Mutex.unlock();
}
for (auto iter = listeners.begin(); iter != listeners.end(); ++iter)
{
// notify each listener
(*iter)->Execute();
}
}
void operator()() const { this->Send(); }
};
// message with 1 parameter and return type
template <typename T, typename A = void>
class Message1 : public MessageBase<MessageAbstractDelegate1<T, A>>
{
public:
typedef MessageBase<MessageAbstractDelegate1<T, A>> Super;
typedef typename Super::ListenerList ListenerList;
void Send(T t) const
{
ListenerList listeners;
{
- this->m_Mutex.Lock();
+ this->m_Mutex.lock();
listeners.assign(this->m_Listeners.begin(), this->m_Listeners.end());
- this->m_Mutex.Unlock();
+ this->m_Mutex.unlock();
}
for (auto iter = listeners.begin(); iter != listeners.end(); ++iter)
{
// notify each listener
(*iter)->Execute(t);
}
}
void operator() (T t) const { this->Send(t); }
};
// message with 2 parameters and return type
template <typename T, typename U, typename A = void>
class Message2 : public MessageBase<MessageAbstractDelegate2<T, U, A>>
{
public:
typedef MessageBase<MessageAbstractDelegate2<T, U, A>> Super;
typedef typename Super::ListenerList ListenerList;
void Send(T t, U u) const
{
ListenerList listeners;
{
- this->m_Mutex.Lock();
+ this->m_Mutex.lock();
listeners.assign(this->m_Listeners.begin(), this->m_Listeners.end());
- this->m_Mutex.Unlock();
+ this->m_Mutex.unlock();
}
for (auto iter = listeners.begin(); iter != listeners.end(); ++iter)
{
// notify each listener
(*iter)->Execute(t, u);
}
}
void operator()(T t, U u) const { this->Send(t, u); }
};
// message with 3 parameters and return type
template <typename T, typename U, typename V, typename A = void>
class Message3 : public MessageBase<MessageAbstractDelegate3<T, U, V, A>>
{
public:
typedef MessageBase<MessageAbstractDelegate3<T, U, V, A>> Super;
typedef typename Super::ListenerList ListenerList;
void Send(T t, U u, V v) const
{
ListenerList listeners;
{
- this->m_Mutex.Lock();
+ this->m_Mutex.lock();
listeners.assign(this->m_Listeners.begin(), this->m_Listeners.end());
- this->m_Mutex.Unlock();
+ this->m_Mutex.unlock();
}
for (typename ListenerList::iterator iter = listeners.begin(); iter != listeners.end(); ++iter)
{
// notify each listener
(*iter)->Execute(t, u, v);
}
}
void operator()(T t, U u, V v) const { this->Send(t, u, v); }
};
// message with 4 parameters and return type
template <typename T, typename U, typename V, typename W, typename A = void>
class Message4 : public MessageBase<MessageAbstractDelegate4<T, U, V, W>>
{
public:
typedef MessageBase<MessageAbstractDelegate4<T, U, V, W, A>> Super;
typedef typename Super::ListenerList ListenerList;
void Send(T t, U u, V v, W w) const
{
ListenerList listeners;
{
- this->m_Mutex.Lock();
+ this->m_Mutex.lock();
listeners.assign(this->m_Listeners.begin(), this->m_Listeners.end());
- this->m_Mutex.Unlock();
+ this->m_Mutex.unlock();
}
for (typename ListenerList::iterator iter = listeners.begin(); iter != listeners.end(); ++iter)
{
// notify each listener
(*iter)->Execute(t, u, v, w);
}
}
void operator()(T t, U u, V v, W w) const { this->Send(t, u, v, w); }
};
/* Here is an example how to use the macros and templates:
*
* // An object to be send around
* class Law
* {
* private:
* std::string m_Description;
*
* public:
*
* Law(const std::string law) : m_Description(law)
* { }
*
* std::string GetDescription() const
* {
* return m_Description;
* }
* };
*
* // The NewtonMachine will issue specific events
* class NewtonMachine
* {
* mitkNewMessageMacro(AnalysisStarted);
* mitkNewMessage1Macro(AnalysisStopped, bool);
* mitkNewMessage1Macro(LawDiscovered, const Law&);
*
* public:
*
* void StartAnalysis()
* {
* // send the "started" signal
* m_AnalysisStartedMessage();
*
* // we found a new law of nature by creating one :-)
* Law massLaw("F=ma");
* m_LawDiscoveredMessage(massLaw);
* }
*
* void StopAnalysis()
* {
* // send the "stop" message with false, indicating
* // that no error occured
* m_AnalysisStoppedMessage(false);
* }
* };
*
* class Observer
* {
* private:
*
* NewtonMachine* m_Machine;
*
* public:
*
* Observer(NewtonMachine* machine) : m_Machine(machine)
* {
* // Add "observers", i.e. function pointers to the machine
* m_Machine->AddAnalysisStartedListener(
* ::mitk::MessageDelegate<Observer>(this, &Observer::MachineStarted));
* m_Machine->AddAnalysisStoppedListener(
* ::mitk::MessageDelegate1<Observer, bool>(this, &Observer::MachineStopped));
* m_Machine->AddLawDiscoveredListener(
* ::mitk::MessageDelegate1<Observer, const Law&>(this, &Observer::LawDiscovered));
* }
*
* ~Observer()
* {
* // Always remove your observers when finished
* m_Machine->RemoveAnalysisStartedListener(
* ::mitk::MessagDelegate<Observer>(this, &Observer::MachineStarted));
* m_Machine->RemoveAnalysisStoppedListener(
* ::mitk::MessageDelegate1<Observer, bool>(this, &Observer::MachineStopped));
* m_Machine->RemoveLawDiscoveredListener(
* ::mitk::MessageDelegate1<Observer, const Law&>(this, &Observer::LawDiscovered));
* }
*
* void MachineStarted()
* {
* std::cout << "Observed machine has started" << std::endl;
* }
*
* void MachineStopped(bool error)
* {
* std::cout << "Observed machine stopped " << (error ? "with an error" : "") << std::endl;
* }
*
* void LawDiscovered(const Law& law)
* {
* std::cout << "New law of nature discovered: " << law.GetDescription() << std::endl;
* }
* };
*
* NewtonMachine newtonMachine;
* Observer observer(&newtonMachine);
*
* // This will send two events to registered observers
* newtonMachine.StartAnalysis();
* // This will send one event to registered observers
* newtonMachine.StopAnalysis();
*
* Another example of how to use these message classes can be
* found in the directory Testing, file mitkMessageTest.cpp
*
*/
} // namespace
#endif
diff --git a/Modules/Core/include/mitkPixelType.h b/Modules/Core/include/mitkPixelType.h
index 3464c89f37..8df6a5e3b8 100644
--- a/Modules/Core/include/mitkPixelType.h
+++ b/Modules/Core/include/mitkPixelType.h
@@ -1,299 +1,299 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkPixelType_h
#define mitkPixelType_h
#include "mitkCommon.h"
#include "mitkPixelTypeTraits.h"
#include <MitkCoreExports.h>
#include <string>
#include <typeinfo>
#include <itkImage.h>
#include <itkImageIOBase.h>
#include <vtkImageData.h>
namespace mitk
{
template <typename T>
std::string PixelComponentTypeToString()
{
return itk::ImageIOBase::GetComponentTypeAsString(itk::ImageIOBase::MapPixelType<T>::CType);
}
template <typename PixelT>
std::string PixelTypeToString()
{
return std::string();
}
/**
* @brief Class for defining the data type of pixels
*
* To obtain additional type information not provided by this class
* itk::ImageIOBase can be used by passing the return value of
* PixelType::GetItkTypeId() to itk::ImageIOBase::SetPixelTypeInfo
* and using the itk::ImageIOBase methods GetComponentType,
* GetComponentTypeAsString, GetPixelType, GetPixelTypeAsString.
* @ingroup Data
*/
class MITKCORE_EXPORT PixelType
{
public:
- typedef itk::ImageIOBase::IOPixelType ItkIOPixelType;
- typedef itk::ImageIOBase::IOComponentType ItkIOComponentType;
+ typedef itk::IOPixelEnum ItkIOPixelType;
+ typedef itk::IOComponentEnum ItkIOComponentType;
PixelType(const mitk::PixelType &aPixelType);
PixelType &operator=(const PixelType &other);
- itk::ImageIOBase::IOPixelType GetPixelType() const;
+ ItkIOPixelType GetPixelType() const;
/**
* \brief Get the \a component type (the scalar (!) type). Each element
* may contain m_NumberOfComponents (more than one) of these scalars.
*
*/
- int GetComponentType() const;
+ ItkIOComponentType GetComponentType() const;
/**
* \brief Returns a string containing the ITK pixel type name.
*/
std::string GetPixelTypeAsString() const;
/**
* \brief Returns a string containing the name of the component.
*/
std::string GetComponentTypeAsString() const;
/**
* \brief Returns a string representing the pixel type and pixel components.
*/
std::string GetTypeAsString() const;
/**
* \brief Get size of the PixelType in bytes
*
* A RGBA PixelType of floats will return 4 * sizeof(float)
*/
size_t GetSize() const;
/**
* \brief Get the number of bits per element (of an
* element)
*
* A vector of double with three components will return
* 8*sizeof(double)*3.
* \sa GetBitsPerComponent
* \sa GetItkTypeId
* \sa GetTypeId
*/
size_t GetBpe() const;
/**
* \brief Get the number of components of which each element consists
*
* Each pixel can consist of multiple components, e.g. RGB.
*/
size_t GetNumberOfComponents() const;
/**
* \brief Get the number of bits per components
* \sa GetBitsPerComponent
*/
size_t GetBitsPerComponent() const;
bool operator==(const PixelType &rhs) const;
bool operator!=(const PixelType &rhs) const;
~PixelType();
private:
friend PixelType MakePixelType(const itk::ImageIOBase *imageIO);
template <typename ComponentT, typename PixelT>
friend PixelType MakePixelType(std::size_t numOfComponents);
template <typename ItkImageType>
friend PixelType MakePixelType();
template <typename ItkImageType>
friend PixelType MakePixelType(size_t);
- PixelType(const int componentType,
- const ItkIOPixelType pixelType,
+ PixelType(ItkIOComponentType componentType,
+ ItkIOPixelType pixelType,
std::size_t bytesPerComponent,
std::size_t numOfComponents,
const std::string &componentTypeName,
const std::string &pixelTypeName);
// default constructor is disabled on purpose
PixelType(void);
/** \brief the \a type_info of the scalar (!) component type. Each element
may contain m_NumberOfComponents (more than one) of these scalars.
*/
- int m_ComponentType;
+ ItkIOComponentType m_ComponentType;
ItkIOPixelType m_PixelType;
std::string m_ComponentTypeName;
std::string m_PixelTypeName;
std::size_t m_NumberOfComponents;
std::size_t m_BytesPerComponent;
};
/**
* @brief deduct the PixelType for a given vtk image
*
* @param vtkimagedata the image the PixelType shall be deducted from
* @return the mitk::PixelType
*/
MITKCORE_EXPORT mitk::PixelType MakePixelType(vtkImageData *vtkimagedata);
/**
* \brief A template method for creating a pixel type.
*/
template <typename ComponentT, typename PixelT>
PixelType MakePixelType(std::size_t numOfComponents)
{
return PixelType(MapPixelType<PixelT, isPrimitiveType<PixelT>::value>::IOComponentType,
MapPixelType<PixelT, isPrimitiveType<PixelT>::value>::IOPixelType,
sizeof(ComponentT),
numOfComponents,
PixelComponentTypeToString<ComponentT>(),
PixelTypeToString<PixelT>());
}
/**
* \brief A template method for creating a pixel type.
*
* @deprecated, use version with one numOfComponents as function argument instead.
*/
template <typename ComponentT, typename PixelT, std::size_t numOfComponents>
PixelType MakePixelType()
{
return MakePixelType<ComponentT, PixelT>(numOfComponents);
}
/**
* \brief A helper template for compile-time checking of supported ITK image types.
*
* Unsupported image types will be marked by template specializations with
* missing definitions;
*/
template <typename ItkImageType>
struct AssertImageTypeIsValid
{
};
// The itk::VariableLengthVector pixel type is not supported in MITK if it is
// used with an itk::Image (it cannot be represented as a mitk::Image object).
// Use a itk::VectorImage instead.
template <typename TPixelType, unsigned int VImageDimension>
struct AssertImageTypeIsValid<itk::Image<itk::VariableLengthVector<TPixelType>, VImageDimension>>;
/** \brief A template method for creating a MITK pixel type na ITK image type
*
* \param numOfComponents The number of components for the pixel type of the ITK image
*/
template <typename ItkImageType>
PixelType MakePixelType(std::size_t numOfComponents)
{
AssertImageTypeIsValid<ItkImageType>();
// define new type, since the ::PixelType is used to distinguish between simple and compound types
typedef typename ItkImageType::PixelType ImportPixelType;
// get the component type ( is either directly ImportPixelType or ImportPixelType::ValueType for compound types )
typedef typename GetComponentType<ImportPixelType>::ComponentType ComponentT;
// The PixelType is the same as the ComponentT for simple types
typedef typename ItkImageType::PixelType PixelT;
// call the constructor
return PixelType(MapPixelType<PixelT, isPrimitiveType<PixelT>::value>::IOComponentType,
MapPixelType<PixelT, isPrimitiveType<PixelT>::value>::IOPixelType,
sizeof(ComponentT),
numOfComponents,
PixelComponentTypeToString<ComponentT>(),
PixelTypeToString<PixelT>());
}
/** \brief A template method for creating a MITK pixel type from an ITK image
* pixel type and dimension
*
* \param numOfComponents The number of components for the pixel type \c TPixelType
*/
template <typename TPixelType, unsigned int VImageDimension>
PixelType MakePixelType(std::size_t numOfComponents)
{
typedef typename ImageTypeTrait<TPixelType, VImageDimension>::ImageType ItkImageType;
return MakePixelType<ItkImageType>(numOfComponents);
}
/** \brief A template method for creating a MITK pixel type from an ITK image
* pixel type and dimension
*
* For images where the number of components of the pixel type is determined at
* runtime (e.g. pixel types like itk::VariableLengthVector<short>) the
* MakePixelType(std::size_t) function must be used.
*/
template <typename ItkImageType>
PixelType MakePixelType()
{
if (ImageTypeTrait<ItkImageType>::IsVectorImage)
{
mitkThrow() << " Variable pixel type given but the length is not specified. Use the parametric MakePixelType( "
"size_t ) method instead.";
}
// Use the InternalPixelType to get "1" for the number of components in case of
// a itk::VectorImage
typedef typename ItkImageType::InternalPixelType PixelT;
const std::size_t numComp = ComponentsTrait<isPrimitiveType<PixelT>::value, ItkImageType>::Size;
// call the constructor
return MakePixelType<ItkImageType>(numComp);
}
/**
* \brief Create a MITK pixel type based on a itk::ImageIOBase object
*/
inline PixelType MakePixelType(const itk::ImageIOBase *imageIO)
{
return mitk::PixelType(imageIO->GetComponentType(),
imageIO->GetPixelType(),
imageIO->GetComponentSize(),
imageIO->GetNumberOfComponents(),
imageIO->GetComponentTypeAsString(imageIO->GetComponentType()),
imageIO->GetPixelTypeAsString(imageIO->GetPixelType()));
}
/** \brief An interface to the MakePixelType method for creating scalar pixel types.
*
* Usage: for example MakeScalarPixelType<short>() for a scalar short image
*/
template <typename T>
PixelType MakeScalarPixelType()
{
return MakePixelType<T, T, 1>();
}
} // namespace mitk
#endif /* mitkPixelType_h */
diff --git a/Modules/Core/include/mitkPixelTypeMultiplex.h b/Modules/Core/include/mitkPixelTypeMultiplex.h
index ed235bd36b..94efdb34de 100644
--- a/Modules/Core/include/mitkPixelTypeMultiplex.h
+++ b/Modules/Core/include/mitkPixelTypeMultiplex.h
@@ -1,173 +1,173 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKPIXELTYPEMULTIPLEX_H
#define MITKPIXELTYPEMULTIPLEX_H
#define mitkPixelTypeMultiplex0(function, ptype) \
\
{ \
- if (ptype.GetComponentType() == itk::ImageIOBase::CHAR) \
+ if (ptype.GetComponentType() == itk::IOComponentEnum::CHAR) \
function<char>(ptype); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::UCHAR) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::UCHAR) \
function<unsigned char>(ptype); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::SHORT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::SHORT) \
function<short>(ptype); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::USHORT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::USHORT) \
function<unsigned short>(ptype); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::INT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::INT) \
function<int>(ptype); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::UINT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::UINT) \
function<unsigned int>(ptype); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::LONG) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::LONG) \
function<long int>(ptype); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::ULONG) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::ULONG) \
function<unsigned long int>(ptype); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::FLOAT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::FLOAT) \
function<float>(ptype); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::DOUBLE) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::DOUBLE) \
function<double>(ptype); \
}
#define mitkPixelTypeMultiplex1(function, ptype, param1) \
\
{ \
- if (ptype.GetComponentType() == itk::ImageIOBase::CHAR) \
+ if (ptype.GetComponentType() == itk::IOComponentEnum::CHAR) \
function<char>(ptype, param1); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::UCHAR) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::UCHAR) \
function<unsigned char>(ptype, param1); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::SHORT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::SHORT) \
function<short>(ptype, param1); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::USHORT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::USHORT) \
function<unsigned short>(ptype, param1); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::INT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::INT) \
function<int>(ptype, param1); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::UINT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::UINT) \
function<unsigned int>(ptype, param1); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::LONG) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::LONG) \
function<long int>(ptype, param1); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::ULONG) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::ULONG) \
function<unsigned long int>(ptype, param1); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::FLOAT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::FLOAT) \
function<float>(ptype, param1); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::DOUBLE) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::DOUBLE) \
function<double>(ptype, param1); \
}
#define mitkPixelTypeMultiplex2(function, ptype, param1, param2) \
\
{ \
- if (ptype.GetComponentType() == itk::ImageIOBase::CHAR) \
+ if (ptype.GetComponentType() == itk::IOComponentEnum::CHAR) \
function<char>(ptype, param1, param2); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::UCHAR) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::UCHAR) \
function<unsigned char>(ptype, param1, param2); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::SHORT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::SHORT) \
function<short>(ptype, param1, param2); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::USHORT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::USHORT) \
function<unsigned short>(ptype, param1, param2); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::INT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::INT) \
function<int>(ptype, param1, param2); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::UINT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::UINT) \
function<unsigned int>(ptype, param1, param2); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::LONG) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::LONG) \
function<long int>(ptype, param1, param2); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::ULONG) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::ULONG) \
function<unsigned long int>(ptype, param1, param2); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::FLOAT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::FLOAT) \
function<float>(ptype, param1, param2); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::DOUBLE) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::DOUBLE) \
function<double>(ptype, param1, param2); \
}
#define mitkPixelTypeMultiplex3(function, ptype, param1, param2, param3) \
\
{ \
- if (ptype.GetComponentType() == itk::ImageIOBase::CHAR) \
+ if (ptype.GetComponentType() == itk::IOComponentEnum::CHAR) \
function<char>(ptype, param1, param2, param3); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::UCHAR) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::UCHAR) \
function<unsigned char>(ptype, param1, param2, param3); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::SHORT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::SHORT) \
function<short>(ptype, param1, param2, param3); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::USHORT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::USHORT) \
function<unsigned short>(ptype, param1, param2, param3); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::INT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::INT) \
function<int>(ptype, param1, param2, param3); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::UINT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::UINT) \
function<unsigned int>(ptype, param1, param2, param3); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::LONG) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::LONG) \
function<long int>(ptype, param1, param2, param3); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::ULONG) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::ULONG) \
function<unsigned long int>(ptype, param1, param2, param3); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::FLOAT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::FLOAT) \
function<float>(ptype, param1, param2, param3); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::DOUBLE) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::DOUBLE) \
function<double>(ptype, param1, param2, param3); \
}
// we have to have a default for, else Clang 3.6.1 complains about problems if 'if evaluates to false'
// therefore if type does not match double is assumed
#define mitkPixelTypeMultiplex4(function, ptype, param1, param2, param3, param4) \
\
{ \
- if (ptype.GetComponentType() == itk::ImageIOBase::CHAR) \
+ if (ptype.GetComponentType() == itk::IOComponentEnum::CHAR) \
function<char>(ptype, param1, param2, param3, param4); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::UCHAR) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::UCHAR) \
function<unsigned char>(ptype, param1, param2, param3, param4); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::SHORT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::SHORT) \
function<short>(ptype, param1, param2, param3, param4); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::USHORT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::USHORT) \
function<unsigned short>(ptype, param1, param2, param3, param4); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::INT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::INT) \
function<int>(ptype, param1, param2, param3, param4); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::UINT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::UINT) \
function<unsigned int>(ptype, param1, param2, param3, param4); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::LONG) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::LONG) \
function<long int>(ptype, param1, param2, param3, param4); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::ULONG) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::ULONG) \
function<unsigned long int>(ptype, param1, param2, param3, param4); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::FLOAT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::FLOAT) \
function<float>(ptype, param1, param2, param3, param4); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::DOUBLE) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::DOUBLE) \
function<double>(ptype, param1, param2, param3, param4); \
else if (true) \
function<double>(ptype, param1, param2, param3, param4); \
}
// we have to have a default for, else Clang 3.6.1 complains about problems if 'if evaluates to false'
// therefore if type does not match double is assumed
#define mitkPixelTypeMultiplex5(function, ptype, param1, param2, param3, param4, param5) \
\
{ \
- if (ptype.GetComponentType() == itk::ImageIOBase::CHAR) \
+ if (ptype.GetComponentType() == itk::IOComponentEnum::CHAR) \
function<char>(ptype, param1, param2, param3, param4, param5); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::UCHAR) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::UCHAR) \
function<unsigned char>(ptype, param1, param2, param3, param4, param5); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::SHORT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::SHORT) \
function<short>(ptype, param1, param2, param3, param4, param5); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::USHORT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::USHORT) \
function<unsigned short>(ptype, param1, param2, param3, param4, param5); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::INT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::INT) \
function<int>(ptype, param1, param2, param3, param4, param5); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::UINT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::UINT) \
function<unsigned int>(ptype, param1, param2, param3, param4, param5); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::LONG) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::LONG) \
function<long int>(ptype, param1, param2, param3, param4, param5); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::ULONG) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::ULONG) \
function<unsigned long int>(ptype, param1, param2, param3, param4, param5); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::FLOAT) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::FLOAT) \
function<float>(ptype, param1, param2, param3, param4, param5); \
- else if (ptype.GetComponentType() == itk::ImageIOBase::DOUBLE) \
+ else if (ptype.GetComponentType() == itk::IOComponentEnum::DOUBLE) \
function<double>(ptype, param1, param2, param3, param4, param5); \
else \
function<double>(ptype, param1, param2, param3, param4, param5); \
}
#endif // MITKPIXELTYPEMULTIPLEX_H
diff --git a/Modules/Core/include/mitkPixelTypeTraits.h b/Modules/Core/include/mitkPixelTypeTraits.h
index 16772df28d..a5f190d33a 100644
--- a/Modules/Core/include/mitkPixelTypeTraits.h
+++ b/Modules/Core/include/mitkPixelTypeTraits.h
@@ -1,271 +1,272 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef PIXELTYPETRAITS_H
#define PIXELTYPETRAITS_H
#include <itkDiffusionTensor3D.h>
#include <itkImage.h>
#include <itkImageIOBase.h>
#include <itkRGBAPixel.h>
#include <itkRGBPixel.h>
#include <itkVectorImage.h>
/** \file mitkPixelTypeTraits.h
*
* The pixel type traits are in general used for compile time resolution of the component type and
* the number of components for compound types like the ones in ItkImageType.
* The default values are used to define the corresponding variable also for scalar types
*/
namespace itk
{
/** Forward declaration of the Variable Length Vector class from ITK */
template <typename TValueType>
class VariableLengthVector;
}
#define MITK_PIXEL_COMPONENT_TYPE(type, ctype, name) \
template <> \
struct mitk::MapPixelComponentType<type> \
{ \
static const int value = ctype; \
} template <> \
std::string mitk::PixelComponentTypeToString() \
{ \
return name; \
}
namespace mitk
{
- static const int PixelUserType = itk::ImageIOBase::MATRIX + 1;
- static const int PixelComponentUserType = itk::ImageIOBase::DOUBLE + 1;
+ static const int PixelUserType = static_cast<int>(itk::IOPixelEnum::MATRIX) + 1;
+ static const int PixelComponentUserType = static_cast<int>(itk::IOComponentEnum::DOUBLE) + 1;
/**
* Maps pixel component types (primitive types like int, short, double, etc. and custom
* types) to and integer constant. Specialize this template for custom types by using the
* #MITK_PIXEL_COMPONENT_TYPE macro.
*/
template <typename T>
struct MapPixelComponentType
{
- static const int value = itk::ImageIOBase::MapPixelType<T>::CType;
+ static const itk::IOComponentEnum value = itk::ImageIOBase::MapPixelType<T>::CType;
};
/**
\brief This is an implementation of a type trait to provide a compile-time check for PixelType used in
the instantiation of an itk::Image
*/
template <typename T>
struct isPrimitiveType
{
static const bool value = false;
};
/** \brief Provides a partial specialization for the \sa isPrimitiveType object */
#define DEFINE_TYPE_PRIMITIVE(_TYPEIN) \
template <> \
struct isPrimitiveType<_TYPEIN> \
{ \
static const bool value = true; \
}
/** \brief Partial specialization (unsigned char) for the isPrimitiveType object */
DEFINE_TYPE_PRIMITIVE(unsigned char);
/** \brief Partial specialization (char) for the isPrimitiveType object */
DEFINE_TYPE_PRIMITIVE(char);
/** \brief Partial specialization (signed char) for the isPrimitiveType object */
DEFINE_TYPE_PRIMITIVE(signed char);
/** \brief Partial specialization (unsigned short) for the isPrimitiveType object */
DEFINE_TYPE_PRIMITIVE(unsigned short);
/** \brief Partial specialization (short) for the isPrimitiveType object */
DEFINE_TYPE_PRIMITIVE(short);
/** \brief Partial specialization (unsigned int) for the isPrimitiveType object */
DEFINE_TYPE_PRIMITIVE(unsigned int);
/** \brief Partial specialization (int) for the isPrimitiveType object */
DEFINE_TYPE_PRIMITIVE(int);
/** \brief Partial specialization (long int) for the isPrimitiveType object */
DEFINE_TYPE_PRIMITIVE(long int);
/** \brief Partial specialization (long unsigned int) for the isPrimitiveType object */
DEFINE_TYPE_PRIMITIVE(long unsigned int);
/** \brief Partial specialization (float) for the isPrimitiveType object */
DEFINE_TYPE_PRIMITIVE(float);
/** \brief Partial specialization (double) for the isPrimitiveType object */
DEFINE_TYPE_PRIMITIVE(double);
template <typename TPixelType, unsigned int VDimension = 0>
struct ImageTypeTrait
{
typedef itk::Image<TPixelType, VDimension> ImageType;
static const bool IsVectorImage = false;
};
template <typename TPixelType, unsigned int VDimension>
struct ImageTypeTrait<itk::VariableLengthVector<TPixelType>, VDimension>
{
typedef itk::VectorImage<TPixelType, VDimension> ImageType;
static const bool IsVectorImage = true;
};
template <typename T>
struct ImageTypeTrait<T, 0>
{
typedef T ImageType;
static const bool IsVectorImage = false;
};
template <typename TPixelType, unsigned int VDimension>
struct ImageTypeTrait<itk::VectorImage<TPixelType, VDimension>, 0>
{
typedef itk::VectorImage<TPixelType, VDimension> ImageType;
static const bool IsVectorImage = true;
};
/** \brief Compile-time trait for resolving the ValueType from an ItkImageType */
template <bool flag, typename T>
struct PixelTypeTrait
{
typedef T ValueType;
};
/** \brief Partial specialization for the PixelTypeTrait
*
* Specialization for the false value. Used to define the value type for non-primitive pixel types
*/
template <typename T>
struct PixelTypeTrait<false, T>
{
typedef typename T::ValueType ValueType;
};
/** \brief Compile time resolving of the type of a component */
template <typename T>
struct GetComponentType
{
typedef typename PixelTypeTrait<isPrimitiveType<T>::value, T>::ValueType ComponentType;
};
/** \brief Object for compile-time resolving of the number of components for given type.
*
* Default value for the component number is 1
*/
template <bool V, typename T>
struct ComponentsTrait
{
static const size_t Size = 1;
};
/** \brief Partial specialization for the ComponentsTraits in case of compound types */
template <typename T>
struct ComponentsTrait<false, T>
{
static const size_t Size = T::ValueType::Length;
};
- typedef itk::ImageIOBase::IOPixelType itkIOPixelType;
+ typedef itk::IOPixelEnum itkIOPixelType;
+ typedef itk::IOComponentEnum itkIOComponentType;
/** \brief Object for compile-time translation of a composite pixel type into an itk::ImageIOBase::IOPixelType
* information
*
* The default value of the IOCompositeType is the UNKNOWNPIXELTYPE, the default value will be used for all but the
* types below with own partial specialization. The values of the IOCompositeType member in the specializations
* correspond
* to the values of the itk::ImageIOBase::IOPixelType enum values.
*/
template <class T>
struct MapCompositePixelType
{
- static const itkIOPixelType IOCompositeType = itk::ImageIOBase::UNKNOWNPIXELTYPE;
+ static const itkIOPixelType IOCompositeType = itkIOPixelType::UNKNOWNPIXELTYPE;
};
//------------------------
// Partial template specialization for fixed-length types
//------------------------
template <class C>
struct MapCompositePixelType<itk::RGBPixel<C>>
{
- static const itkIOPixelType IOCompositeType = itk::ImageIOBase::RGB;
+ static const itkIOPixelType IOCompositeType = itkIOPixelType::RGB;
};
template <class C>
struct MapCompositePixelType<itk::RGBAPixel<C>>
{
- static const itkIOPixelType IOCompositeType = itk::ImageIOBase::RGBA;
+ static const itkIOPixelType IOCompositeType = itkIOPixelType::RGBA;
};
template <class C>
struct MapCompositePixelType<itk::DiffusionTensor3D<C>>
{
- static const itkIOPixelType IOCompositeType = itk::ImageIOBase::DIFFUSIONTENSOR3D;
+ static const itkIOPixelType IOCompositeType = itkIOPixelType::DIFFUSIONTENSOR3D;
};
template <class C>
struct MapCompositePixelType<itk::VariableLengthVector<C>>
{
- static const itkIOPixelType IOCompositeType = itk::ImageIOBase::VECTOR;
+ static const itkIOPixelType IOCompositeType = itkIOPixelType::VECTOR;
};
//------------------------
// Partial template specialization for variable-length types
//------------------------
template <class C, unsigned int N>
struct MapCompositePixelType<itk::Vector<C, N>>
{
- static const itkIOPixelType IOCompositeType = itk::ImageIOBase::VECTOR;
+ static const itkIOPixelType IOCompositeType = itkIOPixelType::VECTOR;
};
template <class C, unsigned int N>
struct MapCompositePixelType<itk::FixedArray<C, N>>
{
- static const itkIOPixelType IOCompositeType = itk::ImageIOBase::COVARIANTVECTOR;
+ static const itkIOPixelType IOCompositeType = itkIOPixelType::COVARIANTVECTOR;
};
template <class C, unsigned int N>
struct MapCompositePixelType<itk::CovariantVector<C, N>>
{
- static const itkIOPixelType IOCompositeType = itk::ImageIOBase::COVARIANTVECTOR;
+ static const itkIOPixelType IOCompositeType = itkIOPixelType::COVARIANTVECTOR;
};
template <class C, unsigned int N>
struct MapCompositePixelType<itk::Matrix<C, N>>
{
- static const itkIOPixelType IOCompositeType = itk::ImageIOBase::MATRIX;
+ static const itkIOPixelType IOCompositeType = itkIOPixelType::MATRIX;
};
/** \brief Object for compile-time translation of a pixel type into an itk::ImageIOBase::IOPixelType information
*
* The first template parameter is the pixel type to be translated, the second parameter determines the processing
* way. For non-primitive types the first template parameter is passed to the MapCompositePixelType object to be
* resolved there
* for primitive types the value is set to SCALAR.
*
* To initalize the flag correctly in compile-time use the \sa isPrimitiveType<T> trait.
*/
template <class T, bool Primitive>
struct MapPixelType
{
static const itkIOPixelType IOPixelType = MapCompositePixelType<T>::IOCompositeType;
- static const int IOComponentType = MapPixelComponentType<typename GetComponentType<T>::ComponentType>::value;
+ static const itkIOComponentType IOComponentType = MapPixelComponentType<typename GetComponentType<T>::ComponentType>::value;
};
/** \brief Partial specialization for setting the IOPixelType for primitive types to SCALAR */
template <class T>
struct MapPixelType<T, true>
{
- static const itkIOPixelType IOPixelType = itk::ImageIOBase::SCALAR;
- static const int IOComponentType = MapPixelComponentType<T>::value;
+ static const itkIOPixelType IOPixelType = itkIOPixelType::SCALAR;
+ static const itkIOComponentType IOComponentType = MapPixelComponentType<T>::value;
};
} // end namespace mitk
#endif // PIXELTYPETRAITS_H
diff --git a/Modules/Core/include/mitkPointSet.h b/Modules/Core/include/mitkPointSet.h
index 4a55101604..d58c5bfe77 100755
--- a/Modules/Core/include/mitkPointSet.h
+++ b/Modules/Core/include/mitkPointSet.h
@@ -1,348 +1,348 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKPointSet_H_HEADER_INCLUDED
#define MITKPointSet_H_HEADER_INCLUDED
#include "mitkBaseData.h"
#include <itkDefaultDynamicMeshTraits.h>
#include <itkMesh.h>
namespace mitk
{
/**
* \brief Data structure which stores a set of points.
*
* 3D points are grouped within a point set; for time resolved usage, one point
* set is created and maintained per time step. A point entry consists of the
* point coordinates and point data.
*
* The point data includes a point ID (unique identifier to address this point
* within the point set), the selection state of the point and the type of
* the point.
*
* For further information about different point types see
* mitk::PointSpecificationType in mitkVector.h.
*
* Inserting a point is accompanied by an event, containing an index. The new
* point is inserted into the list at the specified position. At the same time
* an internal ID is generated and stored for the point. Points at specific time
* steps are accessed by specifying the time step number (which defaults to 0).
*
* The points of itk::PointSet stores the points in a pointContainer
* (MapContainer). The points are best accessed by using a ConstIterator (as
* defined in MapContainer); avoid access via index.
*
* The class internally uses an itk::Mesh for each time step.
*
* \section mitkPointSetDisplayOptions
*
* The default mappers for this data structure are mitk::PointSetGLMapper2D and
* mitk::PointSetVtkMapper3D. See these classes for display options which can
* can be set via properties.
*
* \section Events
*
* PointSet issues the following events, for which observers can register
* (the below events are grouped into a class hierarchy as indicated by
* identation level; e.g. PointSetSizeChangeEvent comprises PointSetAddEvent
* and PointSetRemoveEvent):
*
* <tt>
* PointSetEvent <i>subsumes all PointSet events</i>
* PointSetMoveEvent <i>issued when a point of the PointSet is moved</i>
* PointSetSizeChangeEvent <i>subsumes add and remove events</i>
* PointSetAddEvent <i>issued when a point is added to the PointSet</i>
* PointSetRemoveEvent <i>issued when a point is removed from the PointSet</i>
* </tt>
* \ingroup PSIO
* \ingroup Data
*/
class MITKCORE_EXPORT PointSet : public BaseData
{
public:
mitkClassMacro(PointSet, BaseData);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
typedef mitk::ScalarType CoordinateType;
typedef mitk::ScalarType InterpolationWeightType;
static const unsigned int PointDimension = 3;
static const unsigned int MaxTopologicalDimension = 3;
/**
* \brief struct for data of a point
*/
struct MITKCORE_EXPORT PointDataType
{
unsigned int id; // to give the point a special ID
bool selected; // information about if the point is selected
mitk::PointSpecificationType pointSpec; // specifies the type of the point
bool operator==(const PointDataType &other) const;
};
/**
* \brief cellDataType, that stores all indexes of the lines, that are
* selected e.g.: points A,B and C.Between A and B there is a line with
* index 0. If vector of cellData contains 1 and 2, then the lines between
* B and C and C and A is selected.
*/
typedef std::vector<unsigned int> SelectedLinesType;
typedef SelectedLinesType::iterator SelectedLinesIter;
struct CellDataType
{
// used to set the whole cell on selected
bool selected;
// indexes of selected lines. 0 is between pointId 0 and 1
SelectedLinesType selectedLines;
// is the polygon already finished and closed
bool closed;
};
typedef itk::DefaultDynamicMeshTraits<PointDataType,
PointDimension,
MaxTopologicalDimension,
CoordinateType,
InterpolationWeightType,
CellDataType>
MeshTraits;
typedef itk::Mesh<PointDataType, PointDimension, MeshTraits> MeshType;
typedef MeshType DataType;
typedef Point3D PointType;
typedef DataType::PointIdentifier PointIdentifier;
typedef DataType::PointsContainer PointsContainer;
typedef DataType::PointsContainerIterator PointsIterator;
typedef DataType::PointsContainer::ConstIterator PointsConstIterator;
typedef DataType::PointDataContainer PointDataContainer;
typedef DataType::PointDataContainerIterator PointDataIterator;
typedef DataType::PointDataContainerIterator PointDataConstIterator;
void Expand(unsigned int timeSteps) override;
/** \brief executes the given Operation */
void ExecuteOperation(Operation *operation) override;
/** \brief returns the current size of the point-list */
virtual int GetSize(unsigned int t = 0) const;
virtual unsigned int GetPointSetSeriesSize() const;
/** \brief returns the pointset */
virtual DataType::Pointer GetPointSet(int t = 0) const;
PointsIterator Begin(int t = 0);
PointsConstIterator Begin(int t = 0) const;
PointsIterator End(int t = 0);
PointsConstIterator End(int t = 0) const;
/**
* \brief Get an iterator to the max ID element if existent. Return End() otherwise.
*/
PointsIterator GetMaxId(int t = 0);
/**
* \brief Get the point with ID id in world coordinates
*
* check if the ID exists. If it doesn't exist, then return 0,0,0
*/
PointType GetPoint(PointIdentifier id, int t = 0) const;
/**
* \brief Get the point with ID id in world coordinates
*
* If a point exists for the ID id, the point is returned in the parameter point
* and the method returns true. If the ID does not exist, the method returns false
*/
bool GetPointIfExists(PointIdentifier id, PointType *point, int t = 0) const;
/**
* \brief Set the given point in world coordinate system into the itkPointSet.
*/
void SetPoint(PointIdentifier id, PointType point, int t = 0);
/**
* \brief Set the given point in world coordinate system with the given PointSpecificationType
*/
void SetPoint(PointIdentifier id, PointType point, PointSpecificationType spec, int t = 0);
/**
* \brief Set the given point in world coordinate system into the itkPointSet.
*/
void InsertPoint(PointIdentifier id, PointType point, int t = 0);
/**
* \brief Set the given point in world coordinate system with given PointSpecificationType
*/
void InsertPoint(PointIdentifier id, PointType point, PointSpecificationType spec, int t);
/**
* \brief Insert the given point in world coordinate system with incremented max id at time step t.
*/
PointIdentifier InsertPoint(PointType point, int t = 0);
/**
* \brief Remove point with given id at timestep t, if existent
*/
bool RemovePointIfExists(PointIdentifier id, int t = 0);
/**
* \brief Remove max id point at timestep t and return iterator to precedent point
*/
PointsIterator RemovePointAtEnd(int t = 0);
/**
* \brief Swap a point at the given position (id) with the upper point (moveUpwards=true) or with the lower point
* (moveUpwards=false).
* If upper or lower index does not exist false is returned, if swap was successful true.
*/
bool SwapPointPosition(PointIdentifier id, bool moveUpwards, int t = 0);
/**
* \brief searches a selected point and returns the id of that point.
* If no point is found, then -1 is returned
*/
virtual int SearchSelectedPoint(int t = 0) const;
/** \brief returns true if a point exists at this position */
virtual bool IndexExists(int position, int t = 0) const;
/** \brief to get the state selected/unselected of the point on the
* position
*/
virtual bool GetSelectInfo(int position, int t = 0) const;
virtual void SetSelectInfo(int position, bool selected, int t = 0);
/** \brief to get the type of the point at the position and the moment */
virtual PointSpecificationType GetSpecificationTypeInfo(int position, int t) const;
/** \brief returns the number of selected points */
virtual int GetNumberOfSelected(int t = 0) const;
/**
* \brief searches a point in the list == point +/- distance
*
* \param point is in world coordinates.
* \param distance is in mm.
* \param t
* returns -1 if no point is found
* or the position in the list of the first match
*/
int SearchPoint(Point3D point, ScalarType distance, int t = 0) const;
bool IsEmptyTimeStep(unsigned int t) const override;
// virtual methods, that need to be implemented
void UpdateOutputInformation() override;
void SetRequestedRegionToLargestPossibleRegion() override;
bool RequestedRegionIsOutsideOfTheBufferedRegion() override;
bool VerifyRequestedRegion() override;
void SetRequestedRegion(const itk::DataObject *data) override;
// Method for subclasses
virtual void OnPointSetChange(){};
protected:
mitkCloneMacro(Self);
PointSet();
PointSet(const PointSet &other);
~PointSet() override;
void PrintSelf(std::ostream &os, itk::Indent indent) const override; ///< print content of the object to os
void ClearData() override;
void InitializeEmpty() override;
/** \brief swaps point coordinates and point data of the points with identifiers id1 and id2 */
bool SwapPointContents(PointIdentifier id1, PointIdentifier id2, int t = 0);
typedef std::vector<DataType::Pointer> PointSetSeries;
PointSetSeries m_PointSetSeries;
DataType::PointsContainer::Pointer m_EmptyPointsContainer;
/**
* @brief flag to indicate the right time to call SetBounds
**/
bool m_CalculateBoundingBox;
};
/**
* @brief Equal A function comparing two pointsets for beeing identical.
* @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const
* mitk::PointSet& p1, const mitk::PointSet& p2) instead.
*
* @ingroup MITKTestingAPI
*
* The function compares the Geometry, the size and all points element-wise.
* The parameter eps is a tolarence value for all methods which are internally used for comparion.
*
* @param rightHandSide Compare this against leftHandSide.
* @param leftHandSide Compare this against rightHandSide.
* @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.
* @param checkGeometry if comparing point sets loaded from a file, the geometries might be different and must not be
* compared. In all other cases, you should compare the geometries.
* @return True, if all subsequent comparisons are true, false otherwise
*/
DEPRECATED(MITKCORE_EXPORT bool Equal(const mitk::PointSet *leftHandSide,
const mitk::PointSet *rightHandSide,
mitk::ScalarType eps,
bool verbose,
bool checkGeometry = true));
/**
* @brief Equal A function comparing two pointsets for beeing identical.
*
* @ingroup MITKTestingAPI
*
* The function compares the Geometry, the size and all points element-wise.
* The parameter eps is a tolarence value for all methods which are internally used for comparion.
*
* @param rightHandSide Compare this against leftHandSide.
* @param leftHandSide Compare this against rightHandSide.
* @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.
* @param checkGeometry if comparing point sets loaded from a file, the geometries might be different and must not be
* compared. In all other cases, you should compare the geometries.
* @return True, if all subsequent comparisons are true, false otherwise
*/
MITKCORE_EXPORT bool Equal(const mitk::PointSet &leftHandSide,
const mitk::PointSet &rightHandSide,
mitk::ScalarType eps,
bool verbose,
bool checkGeometry = true);
- itkEventMacro(PointSetEvent, itk::AnyEvent);
- itkEventMacro(PointSetMoveEvent, PointSetEvent);
- itkEventMacro(PointSetSizeChangeEvent, PointSetEvent);
- itkEventMacro(PointSetAddEvent, PointSetSizeChangeEvent);
- itkEventMacro(PointSetRemoveEvent, PointSetSizeChangeEvent);
- itkEventMacro(PointSetExtendTimeRangeEvent, PointSetEvent);
+ itkEventMacroDeclaration(PointSetEvent, itk::AnyEvent);
+ itkEventMacroDeclaration(PointSetMoveEvent, PointSetEvent);
+ itkEventMacroDeclaration(PointSetSizeChangeEvent, PointSetEvent);
+ itkEventMacroDeclaration(PointSetAddEvent, PointSetSizeChangeEvent);
+ itkEventMacroDeclaration(PointSetRemoveEvent, PointSetSizeChangeEvent);
+ itkEventMacroDeclaration(PointSetExtendTimeRangeEvent, PointSetEvent);
} // namespace mitk
#endif /* MITKPointSet_H_HEADER_INCLUDED */
diff --git a/Modules/Core/include/mitkPropertyList.h b/Modules/Core/include/mitkPropertyList.h
index d818815efd..c5832a5888 100644
--- a/Modules/Core/include/mitkPropertyList.h
+++ b/Modules/Core/include/mitkPropertyList.h
@@ -1,254 +1,254 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef PROPERTYLIST_H_HEADER_INCLUDED_C1C77D8D
#define PROPERTYLIST_H_HEADER_INCLUDED_C1C77D8D
#include "mitkBaseProperty.h"
#include "mitkGenericProperty.h"
#include "mitkUIDGenerator.h"
#include "mitkIPropertyOwner.h"
#include <MitkCoreExports.h>
#include <itkObjectFactory.h>
#include <map>
#include <string>
namespace mitk
{
class XMLWriter;
/**
* @brief Key-value list holding instances of BaseProperty
*
* This list is meant to hold an arbitrary list of "properties",
* which should describe the object associated with this list.
*
* Usually you will use PropertyList as part of a DataNode
* object - in this context the properties describe the data object
* held by the DataNode (e.g. whether the object is rendered at
* all, which color is used for rendering, what name should be
* displayed for the object, etc.)
*
* The values in the list are not fixed, you may introduce any kind
* of property that seems useful - all you have to do is inherit
* from BaseProperty.
*
* The list is organized as a key-value pairs, i.e.
*
* \li "name" : pointer to a StringProperty
* \li "visible" : pointer to a BoolProperty
* \li "color" : pointer to a ColorProperty
* \li "volume" : pointer to a FloatProperty
*
* Please see the documentation of SetProperty and ReplaceProperty for two
* quite different semantics. Normally SetProperty is what you want - this
* method will try to change the value of an existing property and will
* not allow you to replace e.g. a ColorProperty with an IntProperty.
*
* Please also regard, that the key of a property must be a none empty string.
* This is a precondition. Setting properties with empty keys will raise an exception.
*
* @ingroup DataManagement
*/
class MITKCORE_EXPORT PropertyList : public itk::Object, public IPropertyOwner
{
public:
mitkClassMacroItkParent(PropertyList, itk::Object);
/**
* Method for creation through the object factory.
*/
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* Map structure to hold the properties: the map key is a string,
* the value consists of the actual property object (BaseProperty).
*/
typedef std::map<std::string, BaseProperty::Pointer> PropertyMap;
typedef std::pair<std::string, BaseProperty::Pointer> PropertyMapElementType;
// IPropertyProvider
BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) const override;
std::vector<std::string> GetPropertyKeys(const std::string &contextName = "", bool includeDefaultContext = false) const override;
std::vector<std::string> GetPropertyContextNames() const override;
// IPropertyOwner
BaseProperty * GetNonConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) override;
void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override;
void RemoveProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override;
/**
* @brief Get a property by its name.
*/
mitk::BaseProperty *GetProperty(const std::string &propertyKey) const;
/**
* @brief Set a property object in the list/map by reference.
*
* The actual OBJECT holding the value of the property is replaced by this function.
* This is useful if you want to change the type of the property, like from BoolProperty to StringProperty.
* Another use is to share one and the same property object among several ProperyList/DataNode objects, which
* makes them appear synchronized.
*/
void ReplaceProperty(const std::string &propertyKey, BaseProperty *property);
/**
* @brief Set a property object in the list/map by reference.
*/
void ConcatenatePropertyList(PropertyList *pList, bool replace = false);
//##Documentation
//## @brief Convenience access method for GenericProperty<T> properties
//## (T being the type of the second parameter)
//## @return @a true property was found
template <typename T>
bool GetPropertyValue(const char *propertyKey, T &value) const
{
GenericProperty<T> *gp = dynamic_cast<GenericProperty<T> *>(GetProperty(propertyKey));
if (gp != nullptr)
{
value = gp->GetValue();
return true;
}
return false;
}
/**
* @brief Convenience method to access the value of a BoolProperty
*/
bool GetBoolProperty(const char *propertyKey, bool &boolValue) const;
/**
* @brief ShortCut for the above method
*/
bool Get(const char *propertyKey, bool &boolValue) const;
/**
* @brief Convenience method to set the value of a BoolProperty
*/
void SetBoolProperty(const char *propertyKey, bool boolValue);
/**
* @brief ShortCut for the above method
*/
void Set(const char *propertyKey, bool boolValue);
/**
* @brief Convenience method to access the value of an IntProperty
*/
bool GetIntProperty(const char *propertyKey, int &intValue) const;
/**
* @brief ShortCut for the above method
*/
bool Get(const char *propertyKey, int &intValue) const;
/**
* @brief Convenience method to set the value of an IntProperty
*/
void SetIntProperty(const char *propertyKey, int intValue);
/**
* @brief ShortCut for the above method
*/
void Set(const char *propertyKey, int intValue);
/**
* @brief Convenience method to access the value of a FloatProperty
*/
bool GetFloatProperty(const char *propertyKey, float &floatValue) const;
/**
* @brief ShortCut for the above method
*/
bool Get(const char *propertyKey, float &floatValue) const;
/**
* @brief Convenience method to set the value of a FloatProperty
*/
void SetFloatProperty(const char *propertyKey, float floatValue);
/**
* @brief ShortCut for the above method
*/
void Set(const char *propertyKey, float floatValue);
/**
* @brief Convenience method to access the value of a DoubleProperty
*/
bool GetDoubleProperty(const char *propertyKey, double &doubleValue) const;
/**
* @brief ShortCut for the above method
*/
bool Get(const char *propertyKey, double &doubleValue) const;
/**
* @brief Convenience method to set the value of a DoubleProperty
*/
void SetDoubleProperty(const char *propertyKey, double doubleValue);
/**
* @brief ShortCut for the above method
*/
void Set(const char *propertyKey, double doubleValue);
/**
* @brief Convenience method to access the value of a StringProperty
*/
bool GetStringProperty(const char *propertyKey, std::string &stringValue) const;
/**
* @brief ShortCut for the above method
*/
bool Get(const char *propertyKey, std::string &stringValue) const;
/**
* @brief Convenience method to set the value of a StringProperty
*/
void SetStringProperty(const char *propertyKey, const char *stringValue);
/**
* @brief ShortCut for the above method
*/
void Set(const char *propertyKey, const char *stringValue);
/**
* @brief ShortCut for the above method
*/
void Set(const char *propertyKey, const std::string &stringValue);
/**
* @brief Get the timestamp of the last change of the map or the last change of one of
* the properties store in the list (whichever is later).
*/
- unsigned long GetMTime() const override;
+ itk::ModifiedTimeType GetMTime() const override;
/**
* @brief Remove a property from the list/map.
*/
bool DeleteProperty(const std::string &propertyKey);
const PropertyMap *GetMap() const { return &m_Properties; }
bool IsEmpty() const { return m_Properties.empty(); }
virtual void Clear();
protected:
PropertyList();
PropertyList(const PropertyList &other);
~PropertyList() override;
/**
* @brief Map of properties.
*/
PropertyMap m_Properties;
private:
itk::LightObject::Pointer InternalClone() const override;
};
} // namespace mitk
#endif /* PROPERTYLIST_H_HEADER_INCLUDED_C1C77D8D */
diff --git a/Modules/Core/include/mitkRenderingManager.h b/Modules/Core/include/mitkRenderingManager.h
index 37f1906f2e..6518d232d8 100644
--- a/Modules/Core/include/mitkRenderingManager.h
+++ b/Modules/Core/include/mitkRenderingManager.h
@@ -1,495 +1,487 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKRENDERINGMANAGER_H
#define MITKRENDERINGMANAGER_H
#include <MitkCoreExports.h>
#include <vtkCallbackCommand.h>
#include <itkObject.h>
#include <itkObjectFactory.h>
#include <mitkProperties.h>
#include <mitkPropertyList.h>
#include <mitkTimeGeometry.h>
#include <mitkAntiAliasing.h>
class vtkRenderWindow;
class vtkObject;
namespace mitk
{
class RenderingManagerFactory;
class BaseGeometry;
class SliceNavigationController;
class BaseRenderer;
class DataStorage;
/**
* \brief Manager for coordinating the rendering process.
*
* RenderingManager is a central instance retrieving and executing
* RenderWindow update requests. Its main purpose is to coordinate
* distributed requests which cannot be aware of each other - lacking the
* knowledge of whether they are really necessary or not. For example, two
* objects might determine that a specific RenderWindow needs to be updated.
* This would result in one unnecessary update, if both executed the update
* on their own.
*
* The RenderingManager addresses this by letting each such object
* <em>request</em> an update, and waiting for other objects to possibly
* issue the same request. The actual update will then only be executed at a
* well-defined point in the main event loop (this may be each time after
* event processing is done).
*
* Convinience methods for updating all RenderWindows which have been
* registered with the RenderingManager exist. If theses methods are not
* used, it is not required to register (add) RenderWindows prior to using
* the RenderingManager.
*
* The methods #ForceImmediateUpdate() and #ForceImmediateUpdateAll() can
* be used to force the RenderWindow update execution without any delay,
* bypassing the request functionality.
*
* The interface of RenderingManager is platform independent. Platform
* specific subclasses have to be implemented, though, to supply an
* appropriate event issueing for controlling the update execution process.
* See method documentation for a description of how this can be done.
*
* \sa TestingRenderingManager An "empty" RenderingManager implementation which
* can be used in tests etc.
*
*/
class MITKCORE_EXPORT RenderingManager : public itk::Object
{
public:
mitkClassMacroItkParent(RenderingManager, itk::Object);
typedef std::vector<vtkRenderWindow *> RenderWindowVector;
typedef std::vector<float> FloatVector;
typedef std::vector<bool> BoolVector;
typedef itk::SmartPointer<DataStorage> DataStoragePointer;
enum RequestType
{
REQUEST_UPDATE_ALL = 0,
REQUEST_UPDATE_2DWINDOWS,
REQUEST_UPDATE_3DWINDOWS
};
static Pointer New();
/** Set the object factory which produces the desired platform specific
* RenderingManager singleton instance. */
static void SetFactory(RenderingManagerFactory *factory);
/** Get the object factory which produces the platform specific
* RenderingManager instances. */
static const RenderingManagerFactory *GetFactory();
/** Returns true if a factory has already been set. */
static bool HasFactory();
/** Get the RenderingManager singleton instance. */
static RenderingManager *GetInstance();
/** Returns true if the singleton instance does already exist. */
static bool IsInstantiated();
/** Adds a RenderWindow. This is required if the methods #RequestUpdateAll
* or #ForceImmediateUpdate are to be used. */
void AddRenderWindow(vtkRenderWindow *renderWindow);
/** Removes a RenderWindow. */
void RemoveRenderWindow(vtkRenderWindow *renderWindow);
/** Get a list of all registered RenderWindows */
const RenderWindowVector &GetAllRegisteredRenderWindows();
/** Requests an update for the specified RenderWindow, to be executed as
* soon as the main loop is ready for rendering. */
void RequestUpdate(vtkRenderWindow *renderWindow);
/** Immediately executes an update of the specified RenderWindow. */
void ForceImmediateUpdate(vtkRenderWindow *renderWindow);
/** Requests all currently registered RenderWindows to be updated.
* If only 2D or 3D windows should be updated, this can be specified
* via the parameter requestType. */
void RequestUpdateAll(RequestType type = REQUEST_UPDATE_ALL);
/** Immediately executes an update of all registered RenderWindows.
* If only 2D or 3D windows should be updated, this can be specified
* via the parameter requestType. */
void ForceImmediateUpdateAll(RequestType type = REQUEST_UPDATE_ALL);
/**
* @brief Initialize the render windows by the aggregated geometry of all objects that are held in
* the data storage.
*
* @param dataStorage The data storage from which the bounding object can be retrieved
*/
virtual void InitializeViewsByBoundingObjects(const DataStorage* dataStorage);
/**
* @brief Initialize the render windows specified by "type" to the given geometry.
*
* Throws an exception if bounding box has 0 extent due to exceeding double precision range.
*
* @param geometry The geometry to be used to initialize / update a
* render window's time and slice navigation controller
* @param type The type of update request:
* - REQUEST_UPDATE_ALL will initialize / update the
* time and slice navigation controller of all retrieved render windows
* - REQUEST_UPDATE_2DWINDOWS will only initialize / update the
* time and slice navigation controller of 2D render windows
* - REQUEST_UPDATE_3DWINDOWS will only initialize / update the
* time and slice navigation controller of 3D render windows
* @param resetCamera If this parameter is set to true, the camera controller will be
* set / fit to the center of the rendered image. If set to false, only the
* the slice navigation controller is reset to the geometry without changing
* the camera view / position.
*/
virtual bool InitializeViews(const BaseGeometry *geometry,
RequestType type = REQUEST_UPDATE_ALL,
bool resetCamera = true);
/**
* @brief Initialize the render windows specified by "type" to the given geometry.
*
* Throws an exception if bounding box has 0 extent due to exceeding double precision range.
*
* @param geometry The geometry to be used to initialize / update a
* render window's time- and slice navigation controller
* @param type The type of update request:
* - REQUEST_UPDATE_ALL will initialize / update the
* time- and slice navigation controller of all retrieved render windows
* - REQUEST_UPDATE_2DWINDOWS will only initialize / update the
* time- and slice navigation controller of 2D render windows
* - REQUEST_UPDATE_3DWINDOWS will only initialize / update the
* time- and slice navigation controller of 3D render windows
* @param resetCamera If this parameter is set to true, the camera controller will be
* set / fit to the center of the rendered image. If set to false, only the
* the slice navigation controller is reset to the geometry without changing
* the camera view / position.
*/
virtual bool InitializeViews(const TimeGeometry *geometry,
RequestType type = REQUEST_UPDATE_ALL,
bool resetCamera = true);
/**
* @brief Initialize the render windows specified by "type" to the default viewing direction
* without updating the geometry information.
*
* @param type The type of update request:
* - REQUEST_UPDATE_ALL will initialize the
* slice navigation controller of all retrieved render windows
* - REQUEST_UPDATE_2DWINDOWS will only initialize the
* slice navigation controller of 2D render windows
* - REQUEST_UPDATE_3DWINDOWS will only initialize the
* slice navigation controller of 3D render windows
*/
virtual bool InitializeViews(RequestType type = REQUEST_UPDATE_ALL);
/**
* @brief Initialize the specified render window to the given geometry.
*
* Throws an exception if bounding box has 0 extent due to exceeding double precision range.
*
* @param renderWindow The specifid render window to update
* @param geometry The geometry to be used to initialize / update the
* render window's time- and slice navigation controller
- * @param initializeGlobalTime If this parameter is set to true, the time navigation controller will be
- * initialized / updated with the given geometry. If set to false, the geometry
- * of the time navigation controller is not updated.
* @param resetCamera If this parameter is set to true, the camera controller will be
* set / fit to the center of the rendered image. If set to false, only the
* the slice navigation controller is reset to the geometry without changing
* the camera view / position.
*/
virtual bool InitializeView(vtkRenderWindow *renderWindow,
const BaseGeometry *geometry,
- bool initializeGlobalTime = true,
bool resetCamera = true);
/**
* @brief Initialize the specified render window to the given geometry.
*
* Throws an exception if bounding box has 0 extent due to exceeding double precision range.
*
* @param renderWindow The specifid render window to update
* @param geometry The geometry to be used to initialize / update the
* render window's time- and slice navigation controller
- * @param initializeGlobalTime If this parameter is set to true, the time navigation controller will be
- * initialized / updated with the given geometry. If set to false, the geometry
- * of the time navigation controller is not updated.
* @param resetCamera If this parameter is set to true, the camera controller will be
* set / fit to the center of the rendered image. If set to false, only the
* the slice navigation controller is reset to the geometry without changing
* the camera view / position.
*/
virtual bool InitializeView(vtkRenderWindow *renderWindow,
const TimeGeometry *geometry,
- bool initializeGlobalTime = true,
bool resetCamera = true);
/**
* @brief Initialize the specified render window to the default viewing direction
* without updating the geometry information.
*
* @param renderWindow The specifid render window to update
*/
virtual bool InitializeView(vtkRenderWindow *renderWindow);
/** Gets the (global) SliceNavigationController responsible for
* time-slicing. */
const SliceNavigationController *GetTimeNavigationController() const;
/** Gets the (global) SliceNavigationController responsible for
* time-slicing. */
SliceNavigationController *GetTimeNavigationController();
~RenderingManager() override;
/** Executes all pending requests. This method has to be called by the
* system whenever a RenderingManager induced request event occurs in
* the system pipeline (see concrete RenderingManager implementations). */
virtual void ExecutePendingRequests();
bool IsRendering() const;
void AbortRendering();
/** En-/Disable LOD increase globally. */
itkSetMacro(LODIncreaseBlocked, bool);
/** En-/Disable LOD increase globally. */
itkGetMacro(LODIncreaseBlocked, bool);
/** En-/Disable LOD increase globally. */
itkBooleanMacro(LODIncreaseBlocked);
/** En-/Disable LOD abort mechanism. */
itkSetMacro(LODAbortMechanismEnabled, bool);
/** En-/Disable LOD abort mechanism. */
itkGetMacro(LODAbortMechanismEnabled, bool);
/** En-/Disable LOD abort mechanism. */
itkBooleanMacro(LODAbortMechanismEnabled);
/** Force a sub-class to start a timer for a pending hires-rendering request */
virtual void StartOrResetTimer(){};
/** To be called by a sub-class from a timer callback */
void ExecutePendingHighResRenderingRequest();
virtual void DoStartRendering(){};
virtual void DoMonitorRendering(){};
virtual void DoFinishAbortRendering(){};
int GetNextLOD(BaseRenderer *renderer);
/** Set current LOD (nullptr means all renderers)*/
void SetMaximumLOD(unsigned int max);
void SetShading(bool state, unsigned int lod);
bool GetShading(unsigned int lod);
void SetClippingPlaneStatus(bool status);
bool GetClippingPlaneStatus();
void SetShadingValues(float ambient, float diffuse, float specular, float specpower);
FloatVector &GetShadingValues();
/** Returns a property list */
PropertyList::Pointer GetPropertyList() const;
/** Returns a property from m_PropertyList */
BaseProperty *GetProperty(const char *propertyKey) const;
/** Sets or adds (if not present) a property in m_PropertyList */
void SetProperty(const char *propertyKey, BaseProperty *propertyValue);
/**
* \brief Setter for internal DataStorage
*
* Sets the DataStorage that is used internally. This instance holds all DataNodes that are
* rendered by the registered BaseRenderers.
*
* If this DataStorage is changed at runtime by calling SetDataStorage(),
* all currently registered BaseRenderers are automatically given the correct instance.
* When a new BaseRenderer is added, it is automatically initialized with the currently active DataStorage.
*/
void SetDataStorage(DataStorage *storage);
/**
* \brief Getter for internal DataStorage
*
* Returns the DataStorage that is used internally. This instance holds all DataNodes that are
* rendered by the registered BaseRenderers.
*/
itkGetMacro(DataStorage, DataStorage*);
itkGetConstMacro(DataStorage, DataStorage*);
/**
* @brief Sets a flag to the given renderwindow to indicated that it has the focus e.g. has been clicked recently.
* @param focusWindow
*/
void SetRenderWindowFocus(vtkRenderWindow *focusWindow);
itkGetMacro(FocusedRenderWindow, vtkRenderWindow *);
itkSetMacro(ConstrainedPanningZooming, bool);
itkGetConstMacro(ConstrainedPanningZooming, bool);
void SetAntiAliasing(AntiAliasing antiAliasing);
itkGetConstMacro(AntiAliasing, AntiAliasing);
protected:
enum
{
RENDERING_INACTIVE = 0,
RENDERING_REQUESTED,
RENDERING_INPROGRESS
};
RenderingManager();
/** Abstract method for generating a system specific event for rendering
* request. This method is called whenever an update is requested */
virtual void GenerateRenderingRequestEvent() = 0;
virtual void InitializePropertyList();
bool m_UpdatePending;
typedef std::map<BaseRenderer *, unsigned int> RendererIntMap;
typedef std::map<BaseRenderer *, bool> RendererBoolMap;
RendererBoolMap m_RenderingAbortedMap;
RendererIntMap m_NextLODMap;
unsigned int m_MaxLOD;
bool m_LODIncreaseBlocked;
bool m_LODAbortMechanismEnabled;
BoolVector m_ShadingEnabled;
bool m_ClippingPlaneEnabled;
FloatVector m_ShadingValues;
static void RenderingStartCallback(vtkObject *caller, unsigned long eid, void *clientdata, void *calldata);
static void RenderingProgressCallback(vtkObject *caller, unsigned long eid, void *clientdata, void *calldata);
static void RenderingEndCallback(vtkObject *caller, unsigned long eid, void *clientdata, void *calldata);
typedef std::map<vtkRenderWindow *, int> RenderWindowList;
RenderWindowList m_RenderWindowList;
RenderWindowVector m_AllRenderWindows;
struct RenderWindowCallbacks
{
vtkCallbackCommand *commands[3u];
};
typedef std::map<vtkRenderWindow *, RenderWindowCallbacks> RenderWindowCallbacksList;
RenderWindowCallbacksList m_RenderWindowCallbacksList;
itk::SmartPointer<SliceNavigationController> m_TimeNavigationController;
static RenderingManager::Pointer s_Instance;
static RenderingManagerFactory *s_RenderingManagerFactory;
PropertyList::Pointer m_PropertyList;
DataStoragePointer m_DataStorage;
bool m_ConstrainedPanningZooming;
private:
/**
* @brief Initialize the specified renderer to the given geometry.
*
* @param baseRenderer The specifid renderer to update
* @param geometry The geometry to be used to initialize / update the
* render window's slice navigation controller
* @param boundingBoxInitialized If this parameter is set to true, the slice navigation controller will be
* initialized / updated with the given geometry. If set to false, the geometry
* of the slice navigation controller is not updated.
* @param mapperID The mapper ID is used to define if the given renderer is a 2D or a 3D renderer.
* In case of a 2D renderer and if "boundingBoxInitialized" is set to true (slice
* navigation controller will be updated with a new geometry), the position of the
* slice navigation controller is set to the center slice.
* @param resetCamera If this parameter is set to true, the camera controller will be
* set / fit to the center of the rendered image. If set to false, only the
* the slice navigation controller is reset to the geometry without changing
* the camera view / position.
*/
void InternalViewInitialization(BaseRenderer *baseRenderer,
const TimeGeometry *geometry,
bool boundingBoxInitialized,
int mapperID,
bool resetCamera);
/**
* @brief Extend the bounding box of the given geometry to make sure the bounding box has an extent bigger than
* zero in any direction.
*
* @param originalGeometry The original geometry to be extended
* @param modifiedGeometry The modified geometry where the new bounds (extended bounding box) are used / set
*/
bool ExtendGeometryForBoundingBox(const TimeGeometry* originalGeometry, TimeGeometry::Pointer& modifiedGeometry);
vtkRenderWindow *m_FocusedRenderWindow;
AntiAliasing m_AntiAliasing;
};
#pragma GCC visibility push(default)
- itkEventMacro(RenderingManagerEvent, itk::AnyEvent);
- itkEventMacro(RenderingManagerViewsInitializedEvent, RenderingManagerEvent);
+ itkEventMacroDeclaration(RenderingManagerEvent, itk::AnyEvent);
+ itkEventMacroDeclaration(RenderingManagerViewsInitializedEvent, RenderingManagerEvent);
#pragma GCC visibility pop
itkEventMacroDeclaration(FocusChangedEvent, itk::AnyEvent);
/**
* Generic RenderingManager implementation for "non-rendering-plattform",
* e.g. for tests. Its factory (TestingRenderingManagerFactory) is
* automatically on start-up and is used by default if not other
* RenderingManagerFactory is instantiated explicitly thereafter.
* (see mitkRenderingManager.cpp)
*/
class MITKCORE_EXPORT TestingRenderingManager : public RenderingManager
{
public:
mitkClassMacro(TestingRenderingManager, RenderingManager);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
protected:
void GenerateRenderingRequestEvent() override {};
};
} // namespace mitk
#endif // MITKRENDERINGMANAGER_H
diff --git a/Modules/Core/include/mitkSliceNavigationController.h b/Modules/Core/include/mitkSliceNavigationController.h
index a7169a6274..113f51cdd1 100644
--- a/Modules/Core/include/mitkSliceNavigationController.h
+++ b/Modules/Core/include/mitkSliceNavigationController.h
@@ -1,477 +1,472 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F
#define SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F
#include "mitkBaseController.h"
#include "mitkMessage.h"
#include "mitkRenderingManager.h"
#include "mitkTimeGeometry.h"
#include <MitkCoreExports.h>
#pragma GCC visibility push(default)
#include <itkEventObject.h>
#pragma GCC visibility pop
#include "mitkDataStorage.h"
#include "mitkRestorePlanePositionOperation.h"
#include <itkCommand.h>
#include <sstream>
namespace mitk
{
-
#define mitkTimeGeometryEventMacro(classname, super) \
class MITKCORE_EXPORT classname : public super \
{ \
public: \
typedef classname Self; \
typedef super Superclass; \
classname(TimeGeometry *aTimeGeometry, unsigned int aPos) : Superclass(aTimeGeometry, aPos) {} \
virtual ~classname() {} \
virtual const char *GetEventName() const { return #classname; } \
virtual bool CheckEvent(const ::itk::EventObject *e) const { return dynamic_cast<const Self *>(e); } \
virtual ::itk::EventObject *MakeObject() const { return new Self(GetTimeGeometry(), GetPos()); } \
private: \
void operator=(const Self &); \
}
class PlaneGeometry;
class BaseGeometry;
class BaseRenderer;
/**
* \brief Controls the selection of the slice the associated BaseRenderer
* will display
*
* A SliceNavigationController takes a BaseGeometry or a TimeGeometry as input world geometry
* (TODO what are the exact requirements?) and generates a TimeGeometry
* as output. The TimeGeometry holds a number of SlicedGeometry3Ds and
* these in turn hold a series of PlaneGeometries. One of these PlaneGeometries is
* selected as world geometry for the BaseRenderers associated to 2D views.
*
* The SliceNavigationController holds has Steppers (one for the slice, a
* second for the time step), which control the selection of a single
* PlaneGeometry from the TimeGeometry. SliceNavigationController generates
* ITK events to tell observers, like a BaseRenderer, when the selected slice
* or timestep changes.
*
* Example:
* \code
* // Initialization
* sliceCtrl = mitk::SliceNavigationController::New();
*
* // Tell the navigator the geometry to be sliced (with geometry a
* // BaseGeometry::ConstPointer)
* sliceCtrl->SetInputWorldGeometry3D(geometry.GetPointer());
*
* // Tell the navigator in which direction it shall slice the data
* sliceCtrl->SetViewDirection(mitk::SliceNavigationController::Axial);
*
* // Connect one or more BaseRenderer to this navigator, i.e.: events sent
* // by the navigator when stepping through the slices (e.g. by
* // sliceCtrl->GetSlice()->Next()) will be received by the BaseRenderer
* // (in this example only slice-changes, see also ConnectGeometryTimeEvent
* // and ConnectGeometryEvents.)
* sliceCtrl->ConnectGeometrySliceEvent(renderer.GetPointer());
*
* //create a world geometry and send the information to the connected renderer(s)
* sliceCtrl->Update();
* \endcode
*
*
* You can connect visible navigators to a SliceNavigationController, e.g., a
* QmitkSliderNavigator (for Qt):
*
* \code
* // Create the visible navigator (a slider with a spin-box)
* QmitkSliderNavigator* navigator =
* new QmitkSliderNavigator(parent, "slidernavigator");
*
* // Connect the navigator to the slice-stepper of the
* // SliceNavigationController. For initialization (position, mininal and
* // maximal values) the values of the SliceNavigationController are used.
* // Thus, accessing methods of a navigator is normally not necessary, since
* // everything can be set via the (Qt-independent) SliceNavigationController.
* // The QmitkStepperAdapter converts the Qt-signals to Qt-independent
* // itk-events.
* new QmitkStepperAdapter(navigator, sliceCtrl->GetSlice(), "navigatoradaptor");
* \endcode
*
* If you do not want that all renderwindows are updated when a new slice is
* selected, you can use a specific RenderingManager, which updates only those
* renderwindows that should be updated. This is sometimes useful when a 3D view
* does not need to be updated when the slices in some 2D views are changed.
* QmitkSliderNavigator (for Qt):
*
* \code
* // create a specific RenderingManager
* mitk::RenderingManager::Pointer myManager = mitk::RenderingManager::New();
*
* // tell the RenderingManager to update only renderwindow1 and renderwindow2
* myManager->AddRenderWindow(renderwindow1);
* myManager->AddRenderWindow(renderwindow2);
*
* // tell the SliceNavigationController of renderwindow1 and renderwindow2
* // to use the specific RenderingManager instead of the global one
* renderwindow1->GetSliceNavigationController()->SetRenderingManager(myManager);
* renderwindow2->GetSliceNavigationController()->SetRenderingManager(myManager);
* \endcode
*
* \todo implement for non-evenly-timed geometry!
* \ingroup NavigationControl
*/
class MITKCORE_EXPORT SliceNavigationController : public BaseController
{
public:
mitkClassMacro(SliceNavigationController, BaseController);
// itkFactorylessNewMacro(Self)
// mitkNewMacro1Param(Self, const char *);
itkNewMacro(Self);
// itkCloneMacro(Self)
/**
* \brief Possible view directions, \a Original will uses
* the PlaneGeometry instances in a SlicedGeometry3D provided
* as input world geometry (by SetInputWorldGeometry3D).
*/
enum ViewDirection
{
Axial,
Sagittal,
Frontal,
Original
};
/**
* \brief Set the input world geometry3D out of which the
* geometries for slicing will be created.
*
* Any previous previous set input geometry (3D or Time) will
* be ignored in future.
*/
void SetInputWorldGeometry3D(const mitk::BaseGeometry *geometry);
itkGetConstObjectMacro(InputWorldGeometry3D, mitk::BaseGeometry);
void SetInputWorldTimeGeometry(const mitk::TimeGeometry *geometry);
itkGetConstObjectMacro(InputWorldTimeGeometry, mitk::TimeGeometry);
/**
* \brief Access the created geometry
*/
itkGetConstObjectMacro(CreatedWorldGeometry, mitk::TimeGeometry);
/**
* \brief Set the desired view directions
*
* \sa ViewDirection
* \sa Update(ViewDirection viewDirection, bool top = true,
* bool frontside = true, bool rotated = false)
*/
itkSetEnumMacro(ViewDirection, ViewDirection);
itkGetEnumMacro(ViewDirection, ViewDirection);
/**
* \brief Set the default view direction
*
* This is used to re-initialize the view direction of the SNC to the
* default value with SetViewDirectionToDefault()
*
* \sa ViewDirection
* \sa Update(ViewDirection viewDirection, bool top = true,
* bool frontside = true, bool rotated = false)
*/
itkSetEnumMacro(DefaultViewDirection, ViewDirection);
itkGetEnumMacro(DefaultViewDirection, ViewDirection);
const char *GetViewDirectionAsString() const;
virtual void SetViewDirectionToDefault();
/**
* \brief Do the actual creation and send it to the connected
* observers (renderers)
*
*/
virtual void Update();
/**
* \brief Extended version of Update, additionally allowing to
* specify the direction/orientation of the created geometry.
*
*/
virtual void Update(ViewDirection viewDirection, bool top = true, bool frontside = true, bool rotated = false);
/**
* \brief Send the created geometry to the connected
* observers (renderers)
*
* Called by Update().
*/
virtual void SendCreatedWorldGeometry();
/**
* \brief Tell observers to re-read the currently selected 2D geometry
*
*/
virtual void SendCreatedWorldGeometryUpdate();
/**
* \brief Send the currently selected slice to the connected
* observers (renderers)
*
* Called by Update().
*/
virtual void SendSlice();
/**
* \brief Send the currently selected time to the connected
* observers (renderers)
*
* Called by Update().
*/
virtual void SendTime();
-#pragma GCC visibility push(default)
- itkEventMacro(UpdateEvent, itk::AnyEvent);
-#pragma GCC visibility pop
-
class MITKCORE_EXPORT TimeGeometryEvent : public itk::AnyEvent
{
public:
typedef TimeGeometryEvent Self;
typedef itk::AnyEvent Superclass;
TimeGeometryEvent(TimeGeometry *aTimeGeometry, unsigned int aPos) : m_TimeGeometry(aTimeGeometry), m_Pos(aPos) {}
~TimeGeometryEvent() override {}
const char *GetEventName() const override { return "TimeGeometryEvent"; }
bool CheckEvent(const ::itk::EventObject *e) const override { return dynamic_cast<const Self *>(e); }
::itk::EventObject *MakeObject() const override { return new Self(m_TimeGeometry, m_Pos); }
TimeGeometry *GetTimeGeometry() const { return m_TimeGeometry; }
unsigned int GetPos() const { return m_Pos; }
private:
TimeGeometry::Pointer m_TimeGeometry;
unsigned int m_Pos;
// TimeGeometryEvent(const Self&);
void operator=(const Self &); // just hide
};
mitkTimeGeometryEventMacro(GeometrySendEvent, TimeGeometryEvent);
mitkTimeGeometryEventMacro(GeometryUpdateEvent, TimeGeometryEvent);
mitkTimeGeometryEventMacro(GeometryTimeEvent, TimeGeometryEvent);
mitkTimeGeometryEventMacro(GeometrySliceEvent, TimeGeometryEvent);
template <typename T>
void ConnectGeometrySendEvent(T *receiver)
{
typedef typename itk::ReceptorMemberCommand<T>::Pointer ReceptorMemberCommandPointer;
ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand<T>::New();
eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometry);
unsigned long tag = AddObserver(GeometrySendEvent(nullptr, 0), eventReceptorCommand);
m_ReceiverToObserverTagsMap[static_cast<void *>(receiver)].push_back(tag);
}
template <typename T>
void ConnectGeometryUpdateEvent(T *receiver)
{
typedef typename itk::ReceptorMemberCommand<T>::Pointer ReceptorMemberCommandPointer;
ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand<T>::New();
eventReceptorCommand->SetCallbackFunction(receiver, &T::UpdateGeometry);
unsigned long tag = AddObserver(GeometryUpdateEvent(nullptr, 0), eventReceptorCommand);
m_ReceiverToObserverTagsMap[static_cast<void *>(receiver)].push_back(tag);
}
template <typename T>
void ConnectGeometrySliceEvent(T *receiver, bool connectSendEvent = true)
{
typedef typename itk::ReceptorMemberCommand<T>::Pointer ReceptorMemberCommandPointer;
ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand<T>::New();
eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometrySlice);
unsigned long tag = AddObserver(GeometrySliceEvent(nullptr, 0), eventReceptorCommand);
m_ReceiverToObserverTagsMap[static_cast<void *>(receiver)].push_back(tag);
if (connectSendEvent)
ConnectGeometrySendEvent(receiver);
}
template <typename T>
void ConnectGeometryTimeEvent(T *receiver, bool connectSendEvent = true)
{
typedef typename itk::ReceptorMemberCommand<T>::Pointer ReceptorMemberCommandPointer;
ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand<T>::New();
eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometryTime);
unsigned long tag = AddObserver(GeometryTimeEvent(nullptr, 0), eventReceptorCommand);
m_ReceiverToObserverTagsMap[static_cast<void *>(receiver)].push_back(tag);
if (connectSendEvent)
ConnectGeometrySendEvent(receiver);
}
template <typename T>
void ConnectGeometryEvents(T *receiver)
{
// connect sendEvent only once
ConnectGeometrySliceEvent(receiver, false);
ConnectGeometryTimeEvent(receiver);
}
// use a templated method to get the right offset when casting to void*
template <typename T>
void Disconnect(T *receiver)
{
auto i = m_ReceiverToObserverTagsMap.find(static_cast<void *>(receiver));
if (i == m_ReceiverToObserverTagsMap.end())
return;
const std::list<unsigned long> &tags = i->second;
for (auto tagIter = tags.begin(); tagIter != tags.end(); ++tagIter)
{
RemoveObserver(*tagIter);
}
m_ReceiverToObserverTagsMap.erase(i);
}
Message1<mitk::Point3D> SetCrosshairEvent;
/**
* \brief To connect multiple SliceNavigationController, we can
* act as an observer ourselves: implemented interface
* \warning not implemented
*/
virtual void SetGeometry(const itk::EventObject &geometrySliceEvent);
/**
* \brief To connect multiple SliceNavigationController, we can
* act as an observer ourselves: implemented interface
*/
virtual void SetGeometrySlice(const itk::EventObject &geometrySliceEvent);
/**
* \brief To connect multiple SliceNavigationController, we can
* act as an observer ourselves: implemented interface
*/
virtual void SetGeometryTime(const itk::EventObject &geometryTimeEvent);
/** \brief Positions the SNC according to the specified point */
void SelectSliceByPoint(const mitk::Point3D &point);
/** \brief Returns the TimeGeometry created by the SNC. */
mitk::TimeGeometry *GetCreatedWorldGeometry();
/** \brief Returns the BaseGeometry of the currently selected time step. */
const mitk::BaseGeometry *GetCurrentGeometry3D();
/** \brief Returns the currently selected Plane in the current
* BaseGeometry (if existent).
*/
const mitk::PlaneGeometry *GetCurrentPlaneGeometry();
/** \brief Sets the BaseRenderer associated with this SNC (if any). While
* the BaseRenderer is not directly used by SNC, this is a convenience
* method to enable BaseRenderer access via the SNC. */
void SetRenderer(BaseRenderer *renderer);
/** \brief Gets the BaseRenderer associated with this SNC (if any). While
* the BaseRenderer is not directly used by SNC, this is a convenience
* method to enable BaseRenderer access via the SNC. Returns nullptr if no
* BaseRenderer has been specified*/
BaseRenderer *GetRenderer() const;
/** \brief Re-orients the slice stack. All slices will be oriented to the given normal vector.
The given point (world coordinates) defines the selected slice.
Careful: The resulting axis vectors are not clearly defined this way. If you want to define them clearly, use
ReorientSlices (const mitk::Point3D &point, const mitk::Vector3D &axisVec0, const mitk::Vector3D &axisVec1).
*/
void ReorientSlices(const mitk::Point3D &point, const mitk::Vector3D &normal);
/** \brief Re-orients the slice stack so that all planes are oriented according to the
* given axis vectors. The given Point eventually defines selected slice.
*/
void ReorientSlices(const mitk::Point3D &point, const mitk::Vector3D &axisVec0, const mitk::Vector3D &axisVec1);
void ExecuteOperation(Operation *operation) override;
/**
* \brief Feature option to lock planes during mouse interaction.
* This option flag disables the mouse event which causes the center
* cross to move near by.
*/
itkSetMacro(SliceLocked, bool);
itkGetMacro(SliceLocked, bool);
itkBooleanMacro(SliceLocked);
/**
* \brief Feature option to lock slice rotation.
*
* This option flag disables separately the rotation of a slice which is
* implemented in mitkSliceRotator.
*/
itkSetMacro(SliceRotationLocked, bool);
itkGetMacro(SliceRotationLocked, bool);
itkBooleanMacro(SliceRotationLocked);
/**
* \brief Adjusts the numerical range of the slice stepper according to
* the current geometry orientation of this SNC's SlicedGeometry.
*/
void AdjustSliceStepperRange();
/** \brief Convenience method that returns the time step currently selected by the controller.*/
TimeStepType GetSelectedTimeStep() const;
/** \brief Convenience method that returns the time point that corresponds to the selected
* time step. The conversion is done using the time geometry of the SliceNavigationController.
* If the time geometry is not yet set, this function will always return 0.0.*/
TimePointType GetSelectedTimePoint() const;
protected:
SliceNavigationController();
~SliceNavigationController() override;
mitk::BaseGeometry::ConstPointer m_InputWorldGeometry3D;
mitk::TimeGeometry::ConstPointer m_InputWorldTimeGeometry;
mitk::TimeGeometry::Pointer m_CreatedWorldGeometry;
ViewDirection m_ViewDirection;
ViewDirection m_DefaultViewDirection;
mitk::RenderingManager::Pointer m_RenderingManager;
mitk::BaseRenderer *m_Renderer;
itkSetMacro(Top, bool);
itkGetMacro(Top, bool);
itkBooleanMacro(Top);
itkSetMacro(FrontSide, bool);
itkGetMacro(FrontSide, bool);
itkBooleanMacro(FrontSide);
itkSetMacro(Rotated, bool);
itkGetMacro(Rotated, bool);
itkBooleanMacro(Rotated);
bool m_Top;
bool m_FrontSide;
bool m_Rotated;
bool m_BlockUpdate;
bool m_SliceLocked;
bool m_SliceRotationLocked;
unsigned int m_OldPos;
typedef std::map<void *, std::list<unsigned long>> ObserverTagsMapType;
ObserverTagsMapType m_ReceiverToObserverTagsMap;
};
} // namespace mitk
#endif /* SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F */
diff --git a/Modules/Core/include/mitkSlicesCoordinator.h b/Modules/Core/include/mitkSlicesCoordinator.h
index 7397f1dda5..99c70fb09f 100644
--- a/Modules/Core/include/mitkSlicesCoordinator.h
+++ b/Modules/Core/include/mitkSlicesCoordinator.h
@@ -1,99 +1,99 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef SLICESCOORDINATOR_H_HEADER_INCLUDED_C1C55A2F
#define SLICESCOORDINATOR_H_HEADER_INCLUDED_C1C55A2F
#include <MitkCoreExports.h>
#include <itkObject.h>
#include <mitkCommon.h>
#include <vector>
namespace mitk
{
class SliceNavigationController;
class Action;
class StateEvent;
#pragma GCC visibility push(default)
- itkEventMacro(SliceRotationEvent, itk::AnyEvent);
+ itkEventMacroDeclaration(SliceRotationEvent, itk::AnyEvent);
#pragma GCC visibility pop
/**
* \brief Coordinates a list of SliceNavigationControllers.
*
* Each SliceNavigationController can select one slice from a
* TimeGeometry. This class (SlicesCoordinator) coordinates several
* SliceNavigationControllers to facilitate e.g. rotation of slices. A new
* class is needed, because for rotation one has to know an axis of rotation.
* Such an axis is most easily determined from the "other slices", which are
* not known by a SliceNavigationController.
*/
class MITKCORE_EXPORT SlicesCoordinator : public itk::Object
{
public:
mitkClassMacroItkParent(SlicesCoordinator, itk::Object);
itkFactorylessNewMacro(Self);
typedef std::vector<SliceNavigationController *> SNCVector;
/** Add to list of managed slices. Check if CreatedWorldGeometry of SNC is
* managable (i.e. there is basically only one planegeometry) */
void AddSliceController(SliceNavigationController *snc);
/** Remove one controller from the internal list */
void RemoveSliceController(SliceNavigationController *snc);
/* Reset all Slices to central slice, no rotation */
// void ResetAllSlices();
/** Set/Get whether planes should stay linked to each other (by fixing
* their relative angle) */
itkSetMacro(LinkPlanes, bool);
itkGetMacro(LinkPlanes, bool);
itkBooleanMacro(LinkPlanes);
/** \brief Resets the mouse cursor (if modified by the SlicesCoordinator)
* to its original state.
*
* Should be used by subclasses and from external application instead
* of using QmitkApplicationCursor directly to avoid conflicts. */
void ResetMouseCursor();
protected:
/** \brief Default Constructor */
SlicesCoordinator();
/** clear list of controllers */
~SlicesCoordinator() override;
/** \brief Sets the specified mouse cursor.
*
* Use this in subclasses instead of using QmitkApplicationCursor directly.
*/
void SetMouseCursor(const char *xpm[], int hotspotX, int hotspotY);
/** for implementation in subclasses */
virtual void OnSliceControllerAdded(SliceNavigationController *snc);
/** for implementation in subclasses */
virtual void OnSliceControllerRemoved(SliceNavigationController *snc);
SNCVector m_SliceNavigationControllers;
bool m_LinkPlanes;
bool m_MouseCursorSet;
};
} // namespace
#endif
diff --git a/Modules/Core/include/mitkStandaloneDataStorage.h b/Modules/Core/include/mitkStandaloneDataStorage.h
index 88e17af581..f026104ac9 100644
--- a/Modules/Core/include/mitkStandaloneDataStorage.h
+++ b/Modules/Core/include/mitkStandaloneDataStorage.h
@@ -1,129 +1,129 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKSTANDALONEDATASTORAGE_H_HEADER_INCLUDED_
#define MITKSTANDALONEDATASTORAGE_H_HEADER_INCLUDED_
#include "itkVectorContainer.h"
#include "mitkDataStorage.h"
#include "mitkMessage.h"
#include <map>
+#include <mutex>
namespace mitk
{
class NodePredicateBase;
class DataNode;
//##Documentation
//## @brief Data management class that handles 'was created by' relations
//##
//## The StandaloneDataStorage provides data storage and management functionality.
//## It handles a 'was created by' relation by associating each data object with a
//## set of source objects that were used to create the new object was created from.
//## Thus, nodes are stored in a noncyclical directed graph data structure.
//## It is derived from mitk::DataStorage and implements its interface,
//## including AddNodeEvent and RemoveNodeEvent.
//## @ingroup StandaloneDataStorage
class MITKCORE_EXPORT StandaloneDataStorage : public mitk::DataStorage
{
public:
mitkClassMacro(StandaloneDataStorage, mitk::DataStorage);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
//##Documentation
//## @brief Adds a DataNode containing a data object to its internal storage
//##
//## This Method adds a new data object to the StandaloneDataStorage. The new object is
//## passed in the first parameter. The second parameter is a set
//## of source objects, that were used to create this object. The new object will have
//## a 'was created from' relation to its source objects.
//## the addition of a new object will fire the notification mechanism.
//## If the node parameter is nullptr or if the DataNode has already been added,
//## an exception will be thrown.
void Add(mitk::DataNode *node, const mitk::DataStorage::SetOfObjects *parents = nullptr) override;
//##Documentation
//## @brief Removes node from the StandaloneDataStorage
//##
void Remove(const mitk::DataNode *node) override;
//##Documentation
//## @brief Checks if a node exists in the StandaloneDataStorage
//##
bool Exists(const mitk::DataNode *node) const override;
//##Documentation
//## @brief returns a set of source objects for a given node that meet the given condition(s).
//##
SetOfObjects::ConstPointer GetSources(const mitk::DataNode *node,
const NodePredicateBase *condition = nullptr,
bool onlyDirectSources = true) const override;
//##Documentation
//## @brief returns a set of derived objects for a given node.
//##
//## GetDerivations() returns a set of objects that are derived from the DataNode node.
//## This means, that node was used to create the returned objects. If the parameter
//## onlyDirectDerivations is set to true (default value), only objects that directly have
//## node as one of their source objects will be returned. Otherwise, objects that are
//## derived from derivations of node are returned too.
//## The derived objects can be filtered with a predicate object as described in the GetSubset()
//## method by providing a predicate as the condition parameter.
SetOfObjects::ConstPointer GetDerivations(const mitk::DataNode *node,
const NodePredicateBase *condition = nullptr,
bool onlyDirectDerivations = true) const override;
//##Documentation
//## @brief returns a set of all data objects that are stored in the data storage
//##
SetOfObjects::ConstPointer GetAll() const override;
- /*ITK Mutex */
- mutable itk::SimpleFastMutexLock m_Mutex;
+ mutable std::mutex m_Mutex;
protected:
//##Documentation
//## @brief noncyclical directed graph data structure to store the nodes with their relation
typedef std::map<mitk::DataNode::ConstPointer, SetOfObjects::ConstPointer> AdjacencyList;
StandaloneDataStorage();
~StandaloneDataStorage() override;
//##Documentation
//## @brief convenience method to check if the object has been initialized (i.e. a data tree has been set)
bool IsInitialized() const;
//##Documentation
//## @brief Traverses the Relation graph and extracts a list of related elements (e.g. Sources or Derivations)
SetOfObjects::ConstPointer GetRelations(const mitk::DataNode *node,
const AdjacencyList &relation,
const NodePredicateBase *condition = nullptr,
bool onlyDirectlyRelated = true) const;
//##Documentation
//## @brief deletes all references to a node in a given relation (used in Remove() and TreeListener)
void RemoveFromRelation(const mitk::DataNode *node, AdjacencyList &relation);
//##Documentation
//## @brief Prints the contents of the StandaloneDataStorage to os. Do not call directly, call ->Print() instead
void PrintSelf(std::ostream &os, itk::Indent indent) const override;
//##Documentation
//## @brief Nodes and their relation are stored in m_SourceNodes
AdjacencyList m_SourceNodes;
//##Documentation
//## @brief Nodes are stored in reverse relation for easier traversal in the opposite direction of the relation
AdjacencyList m_DerivedNodes;
};
} // namespace mitk
#endif /* MITKSTANDALONEDATASTORAGE_H_HEADER_INCLUDED_ */
diff --git a/Modules/Core/include/mitkUtf8Util.h b/Modules/Core/include/mitkUtf8Util.h
new file mode 100644
index 0000000000..fd14bead54
--- /dev/null
+++ b/Modules/Core/include/mitkUtf8Util.h
@@ -0,0 +1,47 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#ifndef mitkUtf8Util_h
+#define mitkUtf8Util_h
+
+#include <MitkCoreExports.h>
+#include <string>
+
+namespace mitk
+{
+ namespace Utf8Util
+ {
+ /**
+ * @brief Convert a string encoded with the current code page to an UTF-8 encoded string (Windows)
+ *
+ * The conversion happens on Windows only. On all other platforms, the input string
+ * is returned unmodified as it is assumed to be UTF-8 encoded already.
+ *
+ * If the conversion fails, a warning is printed and the input string is returned
+ * instead. This matches the behavior before this method was introduced.
+ */
+ MITKCORE_EXPORT std::string Local8BitToUtf8(const std::string& local8BitStr);
+
+ /**
+ * @brief Convert a UTF-8 encoded string to a string encoded with the current code page (Windows)
+ *
+ * The conversion happens on Windows only. On all other platforms, the input string
+ * is returned unmodified as strings are assumed to be always UTF-8 encoded by default.
+ *
+ * If the conversion fails, a warning is printed and the input string is returned
+ * instead. This matches the behavior before this method was introduced.
+ */
+ MITKCORE_EXPORT std::string Utf8ToLocal8Bit(const std::string& utf8Str);
+ }
+}
+
+#endif
diff --git a/Modules/Core/src/Algorithms/mitkExtractSliceFilter2.cpp b/Modules/Core/src/Algorithms/mitkExtractSliceFilter2.cpp
index e448408faa..d6ba0822f6 100644
--- a/Modules/Core/src/Algorithms/mitkExtractSliceFilter2.cpp
+++ b/Modules/Core/src/Algorithms/mitkExtractSliceFilter2.cpp
@@ -1,281 +1,281 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <mitkExtractSliceFilter2.h>
#include <mitkExceptionMacro.h>
#include <mitkImageAccessByItk.h>
#include <mitkImageWriteAccessor.h>
#include <itkBSplineInterpolateImageFunction.h>
#include <itkLinearInterpolateImageFunction.h>
#include <itkNearestNeighborInterpolateImageFunction.h>
#include <limits>
struct mitk::ExtractSliceFilter2::Impl
{
Impl();
~Impl();
PlaneGeometry::Pointer OutputGeometry;
mitk::ExtractSliceFilter2::Interpolator Interpolator;
itk::Object::Pointer InterpolateImageFunction;
};
mitk::ExtractSliceFilter2::Impl::Impl()
: Interpolator(NearestNeighbor)
{
}
mitk::ExtractSliceFilter2::Impl::~Impl()
{
}
namespace
{
template <class TInputImage>
void CreateInterpolateImageFunction(const TInputImage* inputImage, mitk::ExtractSliceFilter2::Interpolator interpolator, itk::Object::Pointer& result)
{
typename itk::InterpolateImageFunction<TInputImage>::Pointer interpolateImageFunction;
switch (interpolator)
{
case mitk::ExtractSliceFilter2::NearestNeighbor:
interpolateImageFunction = itk::NearestNeighborInterpolateImageFunction<TInputImage>::New().GetPointer();
break;
case mitk::ExtractSliceFilter2::Linear:
interpolateImageFunction = itk::LinearInterpolateImageFunction<TInputImage>::New().GetPointer();
break;
case mitk::ExtractSliceFilter2::Cubic:
{
auto bSplineInterpolateImageFunction = itk::BSplineInterpolateImageFunction<TInputImage>::New();
bSplineInterpolateImageFunction->SetSplineOrder(2);
interpolateImageFunction = bSplineInterpolateImageFunction.GetPointer();
break;
}
default:
mitkThrow() << "Interplator is unknown.";
}
interpolateImageFunction->SetInputImage(inputImage);
result = interpolateImageFunction.GetPointer();
}
template <typename TPixel, unsigned int VImageDimension>
void GenerateData(const itk::Image<TPixel, VImageDimension>* inputImage, mitk::Image* outputImage, const mitk::ExtractSliceFilter2::OutputImageRegionType& outputRegion, itk::Object* interpolateImageFunction)
{
typedef itk::Image<TPixel, VImageDimension> TInputImage;
typedef itk::InterpolateImageFunction<TInputImage> TInterpolateImageFunction;
auto outputGeometry = outputImage->GetSlicedGeometry()->GetPlaneGeometry(0);
auto interpolator = static_cast<TInterpolateImageFunction*>(interpolateImageFunction);
auto origin = outputGeometry->GetOrigin();
auto spacing = outputGeometry->GetSpacing();
auto xDirection = outputGeometry->GetAxisVector(0);
auto yDirection = outputGeometry->GetAxisVector(1);
xDirection.Normalize();
yDirection.Normalize();
auto spacingAlongXDirection = xDirection * spacing[0];
auto spacingAlongYDirection = yDirection * spacing[1];
const std::size_t pixelSize = outputImage->GetPixelType().GetSize();
const std::size_t width = outputGeometry->GetExtent(0);
const std::size_t xBegin = outputRegion.GetIndex(0);
const std::size_t yBegin = outputRegion.GetIndex(1);
const std::size_t xEnd = xBegin + outputRegion.GetSize(0);
const std::size_t yEnd = yBegin + outputRegion.GetSize(1);
mitk::ImageWriteAccessor writeAccess(outputImage, nullptr, mitk::ImageAccessorBase::IgnoreLock);
auto data = static_cast<char*>(writeAccess.GetData());
const TPixel backgroundPixel = std::numeric_limits<TPixel>::lowest();
TPixel pixel;
itk::ContinuousIndex<mitk::ScalarType, 3> index;
mitk::Point3D yPoint;
mitk::Point3D point;
for (std::size_t y = yBegin; y < yEnd; ++y)
{
yPoint = origin + spacingAlongYDirection * y;
for (std::size_t x = xBegin; x < xEnd; ++x)
{
point = yPoint + spacingAlongXDirection * x;
if (inputImage->TransformPhysicalPointToContinuousIndex(point, index))
{
pixel = interpolator->EvaluateAtContinuousIndex(index);
memcpy(static_cast<void*>(data + pixelSize * (width * y + x)), static_cast<const void*>(&pixel), pixelSize);
}
else
{
memcpy(static_cast<void*>(data + pixelSize * (width * y + x)), static_cast<const void*>(&backgroundPixel), pixelSize);
}
}
}
}
void VerifyInputImage(const mitk::Image* inputImage)
{
auto dimension = inputImage->GetDimension();
if (3 != dimension)
mitkThrow() << "Input images with " << dimension << " dimensions are not supported.";
if (!inputImage->IsInitialized())
mitkThrow() << "Input image is not initialized.";
if (!inputImage->IsVolumeSet())
mitkThrow() << "Input image volume is not set.";
auto geometry = inputImage->GetGeometry();
if (nullptr == geometry || !geometry->IsValid())
mitkThrow() << "Input image has invalid geometry.";
if (!geometry->GetImageGeometry())
mitkThrow() << "Geometry of input image is not an image geometry.";
}
void VerifyOutputGeometry(const mitk::PlaneGeometry* outputGeometry)
{
if (nullptr == outputGeometry)
mitkThrow() << "Output geometry is not set.";
if (!outputGeometry->GetImageGeometry())
mitkThrow() << "Output geometry is not an image geometry.";
}
}
mitk::ExtractSliceFilter2::ExtractSliceFilter2()
: m_Impl(new Impl)
{
}
mitk::ExtractSliceFilter2::~ExtractSliceFilter2()
{
delete m_Impl;
}
void mitk::ExtractSliceFilter2::AllocateOutputs()
{
const auto* inputImage = this->GetInput();
const auto* outputGeometry = this->GetOutputGeometry();
auto outputImage = this->GetOutput();
auto pixelType = inputImage->GetPixelType();
outputImage->Initialize(pixelType, 1, *outputGeometry);
auto data = new char[static_cast<std::size_t>(pixelType.GetSize() * outputGeometry->GetExtent(0) * outputGeometry->GetExtent(1))];
try
{
if (!outputImage->SetImportVolume(data, 0, 0, mitk::Image::ReferenceMemory))
throw;
}
catch (...)
{
delete[] data;
}
}
/*void mitk::ExtractSliceFilter2::BeforeThreadedGenerateData()
{
if (nullptr != m_Impl->InterpolateImageFunction && this->GetInput()->GetMTime() < this->GetMTime())
return;
const auto* inputImage = this->GetInput();
AccessFixedDimensionByItk_2(inputImage, CreateInterpolateImageFunction, 3, this->GetInterpolator(), m_Impl->InterpolateImageFunction);
}
void mitk::ExtractSliceFilter2::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, itk::ThreadIdType)
{
const auto* inputImage = this->GetInput();
AccessFixedDimensionByItk_3(inputImage, ::GenerateData, 3, this->GetOutput(), outputRegionForThread, m_Impl->InterpolateImageFunction);
}*/
void mitk::ExtractSliceFilter2::GenerateData()
{
if (nullptr != m_Impl->InterpolateImageFunction && this->GetInput()->GetMTime() < this->GetMTime())
return;
const auto* inputImage = this->GetInput();
AccessFixedDimensionByItk_2(inputImage, CreateInterpolateImageFunction, 3, this->GetInterpolator(), m_Impl->InterpolateImageFunction);
this->AllocateOutputs();
auto outputRegion = this->GetOutput()->GetLargestPossibleRegion();
AccessFixedDimensionByItk_3(inputImage, ::GenerateData, 3, this->GetOutput(), outputRegion, m_Impl->InterpolateImageFunction);
}
void mitk::ExtractSliceFilter2::SetInput(const InputImageType* image)
{
if (this->GetInput() == image)
return;
Superclass::SetInput(image);
m_Impl->InterpolateImageFunction = nullptr;
}
void mitk::ExtractSliceFilter2::SetInput(unsigned int index, const InputImageType* image)
{
if (0 != index)
mitkThrow() << "Input index " << index << " is invalid.";
this->SetInput(image);
}
const mitk::PlaneGeometry* mitk::ExtractSliceFilter2::GetOutputGeometry() const
{
return m_Impl->OutputGeometry;
}
void mitk::ExtractSliceFilter2::SetOutputGeometry(PlaneGeometry::Pointer outputGeometry)
{
if (m_Impl->OutputGeometry != outputGeometry)
{
m_Impl->OutputGeometry = outputGeometry;
this->Modified();
}
}
mitk::ExtractSliceFilter2::Interpolator mitk::ExtractSliceFilter2::GetInterpolator() const
{
return m_Impl->Interpolator;
}
void mitk::ExtractSliceFilter2::SetInterpolator(Interpolator interpolator)
{
if (m_Impl->Interpolator != interpolator)
{
m_Impl->Interpolator = interpolator;
m_Impl->InterpolateImageFunction = nullptr;
this->Modified();
}
}
-void mitk::ExtractSliceFilter2::VerifyInputInformation()
+void mitk::ExtractSliceFilter2::VerifyInputInformation() const
{
Superclass::VerifyInputInformation();
VerifyInputImage(this->GetInput());
VerifyOutputGeometry(this->GetOutputGeometry());
}
diff --git a/Modules/Core/src/Algorithms/mitkHistogramGenerator.cpp b/Modules/Core/src/Algorithms/mitkHistogramGenerator.cpp
index 3907e3b40b..6802fb3c62 100644
--- a/Modules/Core/src/Algorithms/mitkHistogramGenerator.cpp
+++ b/Modules/Core/src/Algorithms/mitkHistogramGenerator.cpp
@@ -1,125 +1,122 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#if (_MSC_VER == 1200)
#include <itkFixedCenterOfRotationAffineTransform.h>
#endif
#include "mitkHistogramGenerator.h"
#include "mitkImageAccessByItk.h"
#include "mitkImageTimeSelector.h"
//
// The new ITK Statistics framework has
// a class with the same functionality as
// MITKScalarImageToHistogramGenerator.h, but
// no longer has the classis the MITK class depends on.
#if !defined(ITK_USE_REVIEW_STATISTICS)
#include "itkMITKScalarImageToHistogramGenerator.h"
#else
#include "itkScalarImageToHistogramGenerator.h"
#endif
mitk::HistogramGenerator::HistogramGenerator() : m_Image(nullptr), m_Size(256), m_Histogram(nullptr)
{
}
mitk::HistogramGenerator::~HistogramGenerator()
{
}
template <typename TPixel, unsigned int VImageDimension>
void InternalCompute(itk::Image<TPixel, VImageDimension> *itkImage,
const mitk::HistogramGenerator *mitkHistoGenerator,
mitk::HistogramGenerator::HistogramType::ConstPointer &histogram)
{
#if !defined(ITK_USE_REVIEW_STATISTICS)
typedef itk::Statistics::MITKScalarImageToHistogramGenerator<itk::Image<TPixel, VImageDimension>, double>
HistogramGeneratorType;
#else
typedef itk::Statistics::ScalarImageToHistogramGenerator<itk::Image<TPixel, VImageDimension>> HistogramGeneratorType;
#endif
typename HistogramGeneratorType::Pointer histogramGenerator = HistogramGeneratorType::New();
histogramGenerator->SetInput(itkImage);
histogramGenerator->SetNumberOfBins(mitkHistoGenerator->GetSize());
// histogramGenerator->SetMarginalScale( 10.0 );
histogramGenerator->Compute();
histogram = histogramGenerator->GetOutput();
}
void mitk::HistogramGenerator::ComputeHistogram()
{
if ((m_Histogram.IsNull()) || (m_Histogram->GetMTime() < m_Image->GetMTime()))
{
const_cast<mitk::Image *>(m_Image.GetPointer())->SetRequestedRegionToLargestPossibleRegion(); //@todo without this,
// Image::GetScalarMin
// does not work for
// dim==3 (including
// sliceselector!)
const_cast<mitk::Image *>(m_Image.GetPointer())->Update();
mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New();
timeSelector->SetInput(m_Image);
timeSelector->SetTimeNr(0);
timeSelector->UpdateLargestPossibleRegion();
AccessByItk_n(timeSelector->GetOutput(), InternalCompute, (this, m_Histogram));
}
// debug code
/*
MITK_INFO << "Histogram modfied 1" << m_Histogram->GetMTime() << std::endl;
m_Histogram->Modified();
MITK_INFO << "Histogram modfied 2" << m_Histogram->GetMTime() << std::endl;
MITK_INFO << "Image modfied" << m_Image->GetMTime() << std::endl;
const unsigned int histogramSize = m_Histogram->Size();
MITK_INFO << "Histogram size " << histogramSize << std::endl;
HistogramType::ConstIterator itr = GetHistogram()->Begin();
HistogramType::ConstIterator end = GetHistogram()->End();
int bin = 0;
while( itr != end )
{
MITK_INFO << "bin = " << GetHistogram()->GetBinMin(0,bin) << "--" << GetHistogram()->GetBinMax(0,bin) << " frequency
= ";
MITK_INFO << itr.GetFrequency() << std::endl;
++itr;
++bin;
}
*/
}
float mitk::HistogramGenerator::GetMaximumFrequency() const
{
return CalculateMaximumFrequency(this->m_Histogram);
-};
+}
float mitk::HistogramGenerator::CalculateMaximumFrequency(const HistogramType *histogram)
{
HistogramType::ConstIterator itr = histogram->Begin();
HistogramType::ConstIterator end = histogram->End();
float maxFreq = 0;
while (itr != end)
{
- maxFreq = vnl_math_max(maxFreq,
- // get rid of ambiguity with type signature
- // for vnl_math_max
- static_cast<float>(itr.GetFrequency()));
+ maxFreq = std::max(maxFreq, static_cast<float>(itr.GetFrequency()));
++itr;
}
return maxFreq;
-};
+}
diff --git a/Modules/Core/src/Algorithms/mitkImageSource.cpp b/Modules/Core/src/Algorithms/mitkImageSource.cpp
index 8ebc72332d..7a4dc03182 100644
--- a/Modules/Core/src/Algorithms/mitkImageSource.cpp
+++ b/Modules/Core/src/Algorithms/mitkImageSource.cpp
@@ -1,199 +1,201 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkImageSource.h"
#include "mitkImageVtkReadAccessor.h"
#include "mitkImageVtkWriteAccessor.h"
+#include <itkMultiThreaderBase.h>
+
mitk::ImageSource::ImageSource()
{
// Create the output. We use static_cast<> here because we know the default
// output must be of type TOutputImage
OutputImageType::Pointer output = static_cast<OutputImageType *>(this->MakeOutput(0).GetPointer());
Superclass::SetNumberOfRequiredOutputs(1);
Superclass::SetNthOutput(0, output.GetPointer());
}
itk::DataObject::Pointer mitk::ImageSource::MakeOutput(DataObjectPointerArraySizeType /*idx*/)
{
return static_cast<itk::DataObject *>(mitk::Image::New().GetPointer());
}
itk::DataObject::Pointer mitk::ImageSource::MakeOutput(const DataObjectIdentifierType &name)
{
itkDebugMacro("MakeOutput(" << name << ")");
if (this->IsIndexedOutputName(name))
{
return this->MakeOutput(this->MakeIndexFromOutputName(name));
}
return static_cast<itk::DataObject *>(mitk::Image::New().GetPointer());
}
//----------------------------------------------------------------------------
unsigned int mitk::ImageSource::SplitRequestedRegion(unsigned int i,
unsigned int num,
OutputImageRegionType &splitRegion)
{
// Get the output pointer
OutputImageType *outputPtr = this->GetOutput();
const SlicedData::SizeType &requestedRegionSize = outputPtr->GetRequestedRegion().GetSize();
int splitAxis;
SlicedData::IndexType splitIndex;
SlicedData::SizeType splitSize;
// Initialize the splitRegion to the output requested region
splitRegion = outputPtr->GetRequestedRegion();
splitIndex = splitRegion.GetIndex();
splitSize = splitRegion.GetSize();
// split on the outermost dimension available
splitAxis = outputPtr->GetDimension() - 1;
while (requestedRegionSize[splitAxis] == 1)
{
--splitAxis;
if (splitAxis < 0)
{ // cannot split
itkDebugMacro(" Cannot Split");
return 1;
}
}
// determine the actual number of pieces that will be generated
SlicedData::SizeType::SizeValueType range = requestedRegionSize[splitAxis];
auto valuesPerThread = itk::Math::Ceil<unsigned int>(range / (double)num);
unsigned int maxThreadIdUsed = itk::Math::Ceil<unsigned int>(range / (double)valuesPerThread) - 1;
// Split the region
if (i < maxThreadIdUsed)
{
splitIndex[splitAxis] += i * valuesPerThread;
splitSize[splitAxis] = valuesPerThread;
}
if (i == maxThreadIdUsed)
{
splitIndex[splitAxis] += i * valuesPerThread;
// last thread needs to process the "rest" dimension being split
splitSize[splitAxis] = splitSize[splitAxis] - i * valuesPerThread;
}
// set the split region ivars
splitRegion.SetIndex(splitIndex);
splitRegion.SetSize(splitSize);
itkDebugMacro(" Split Piece: " << splitRegion);
return maxThreadIdUsed + 1;
}
//----------------------------------------------------------------------------
void mitk::ImageSource::AllocateOutputs()
{
OutputImagePointer outputPtr;
// Allocate the output memory
for (unsigned int i = 0; i < this->GetNumberOfOutputs(); i++)
{
outputPtr = this->GetOutput(i);
// outputPtr->SetBufferedRegion(outputPtr->GetRequestedRegion()); @FIXME???
// outputPtr->Allocate(); @FIXME???
}
}
//----------------------------------------------------------------------------
void mitk::ImageSource::GenerateData()
{
// Call a method that can be overriden by a subclass to allocate
// memory for the filter's outputs
this->AllocateOutputs();
// Call a method that can be overridden by a subclass to perform
// some calculations prior to splitting the main computations into
// separate threads
this->BeforeThreadedGenerateData();
// Set up the multithreaded processing
ThreadStruct str;
str.Filter = this;
- this->GetMultiThreader()->SetNumberOfThreads(this->GetNumberOfThreads());
+ this->GetMultiThreader()->SetNumberOfWorkUnits(this->GetNumberOfWorkUnits());
this->GetMultiThreader()->SetSingleMethod(this->ThreaderCallback, &str);
// multithread the execution
this->GetMultiThreader()->SingleMethodExecute();
// Call a method that can be overridden by a subclass to perform
// some calculations after all the threads have completed
this->AfterThreadedGenerateData();
}
//----------------------------------------------------------------------------
// The execute method created by the subclass.
void mitk::ImageSource::ThreadedGenerateData(const OutputImageRegionType &, itk::ThreadIdType)
{
itkExceptionMacro("subclass should override this method!!!");
}
// Callback routine used by the threading library. This routine just calls
// the ThreadedGenerateData method after setting the correct region for this
// thread.
-ITK_THREAD_RETURN_TYPE mitk::ImageSource::ThreaderCallback(void *arg)
+itk::ITK_THREAD_RETURN_TYPE mitk::ImageSource::ThreaderCallback(void *arg)
{
ThreadStruct *str;
itk::ThreadIdType total, threadId, threadCount;
- threadId = ((itk::MultiThreader::ThreadInfoStruct *)(arg))->ThreadID;
- threadCount = ((itk::MultiThreader::ThreadInfoStruct *)(arg))->NumberOfThreads;
+ threadId = ((itk::MultiThreaderBase::WorkUnitInfo *)(arg))->WorkUnitID;
+ threadCount = ((itk::MultiThreaderBase::WorkUnitInfo *)(arg))->NumberOfWorkUnits;
- str = (ThreadStruct *)(((itk::MultiThreader::ThreadInfoStruct *)(arg))->UserData);
+ str = (ThreadStruct *)(((itk::MultiThreaderBase::WorkUnitInfo *)(arg))->UserData);
// execute the actual method with appropriate output region
// first find out how many pieces extent can be split into.
SlicedData::RegionType splitRegion;
total = str->Filter->SplitRequestedRegion(threadId, threadCount, splitRegion);
if (threadId < total)
{
str->Filter->ThreadedGenerateData(splitRegion, threadId);
}
// else
// {
// otherwise don't use this thread. Sometimes the threads dont
// break up very well and it is just as efficient to leave a
// few threads idle.
// }
- return ITK_THREAD_RETURN_VALUE;
+ return itk::ITK_THREAD_RETURN_DEFAULT_VALUE;
}
void mitk::ImageSource::PrepareOutputs()
{
Superclass::PrepareOutputs();
}
vtkImageData *mitk::ImageSource::GetVtkImageData()
{
Update();
return GetOutput()->GetVtkImageData();
}
const vtkImageData *mitk::ImageSource::GetVtkImageData() const
{
return GetOutput()->GetVtkImageData();
}
mitkBaseDataSourceGetOutputDefinitions(mitk::ImageSource)
diff --git a/Modules/Core/src/Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp b/Modules/Core/src/Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp
index 5de2fdba82..130adf3ce4 100644
--- a/Modules/Core/src/Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp
+++ b/Modules/Core/src/Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp
@@ -1,161 +1,161 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// mitk includes
#include "mitkMultiComponentImageDataComparisonFilter.h"
#include "mitkImagePixelReadAccessor.h"
#include "mitkImageReadAccessor.h"
// other includes
// #include <omp.h>
namespace mitk
{
MultiComponentImageDataComparisonFilter::MultiComponentImageDataComparisonFilter()
: ImageToImageFilter(), m_Tolerance(0.0f), m_CompareResult(false), m_CompareDetails(nullptr)
{
this->SetNumberOfRequiredInputs(2);
}
MultiComponentImageDataComparisonFilter::~MultiComponentImageDataComparisonFilter() {}
bool MultiComponentImageDataComparisonFilter::GetResult(double threshold)
{
if (!m_CompareResult)
{
return false;
}
if (m_CompareDetails->m_PixelsWithDifference > threshold)
{
return false;
}
return true;
}
void MultiComponentImageDataComparisonFilter::SetValidImage(const Image *_arg) { this->SetInput(0, _arg); }
void MultiComponentImageDataComparisonFilter::SetTestImage(const Image *_arg) { this->SetInput(1, _arg); }
const Image *MultiComponentImageDataComparisonFilter::GetValidImage() { return this->GetInput(0); }
const Image *MultiComponentImageDataComparisonFilter::GetTestImage() { return this->GetInput(1); }
void MultiComponentImageDataComparisonFilter::SetCompareFilterResult(CompareFilterResults *results)
{
m_CompareDetails = results;
}
CompareFilterResults *MultiComponentImageDataComparisonFilter::GetCompareFilterResult() { return m_CompareDetails; }
void MultiComponentImageDataComparisonFilter::GenerateData()
{
// check inputs
const Image *testInput = this->GetTestImage();
const Image *validInput = this->GetValidImage();
// Generally this filter is part of the mitk::Image::Equal() method and only checks the equality of the image data
// so no further image type comparison is performed!
// CAVE: If the images differ in a parameter other then the image data, the filter may fail!!
PixelType type = validInput->GetPixelType();
- if (type.GetComponentType() == itk::ImageIOBase::CHAR)
+ if (type.GetComponentType() == itk::IOComponentEnum::CHAR)
{
CompareMultiComponentImage<char>(testInput, validInput);
}
- else if (type.GetComponentType() == itk::ImageIOBase::UCHAR)
+ else if (type.GetComponentType() == itk::IOComponentEnum::UCHAR)
{
CompareMultiComponentImage<unsigned char>(testInput, validInput);
}
- else if (type.GetComponentType() == itk::ImageIOBase::INT)
+ else if (type.GetComponentType() == itk::IOComponentEnum::INT)
{
CompareMultiComponentImage<int>(testInput, validInput);
}
- else if (type.GetComponentType() == itk::ImageIOBase::UINT)
+ else if (type.GetComponentType() == itk::IOComponentEnum::UINT)
{
CompareMultiComponentImage<unsigned int>(testInput, validInput);
}
- else if (type.GetComponentType() == itk::ImageIOBase::SHORT)
+ else if (type.GetComponentType() == itk::IOComponentEnum::SHORT)
{
CompareMultiComponentImage<short>(testInput, validInput);
}
- else if (type.GetComponentType() == itk::ImageIOBase::USHORT)
+ else if (type.GetComponentType() == itk::IOComponentEnum::USHORT)
{
CompareMultiComponentImage<unsigned short>(testInput, validInput);
}
- else if (type.GetComponentType() == itk::ImageIOBase::LONG)
+ else if (type.GetComponentType() == itk::IOComponentEnum::LONG)
{
CompareMultiComponentImage<long>(testInput, validInput);
}
- else if (type.GetComponentType() == itk::ImageIOBase::ULONG)
+ else if (type.GetComponentType() == itk::IOComponentEnum::ULONG)
{
CompareMultiComponentImage<unsigned long>(testInput, validInput);
}
- else if (type.GetComponentType() == itk::ImageIOBase::FLOAT)
+ else if (type.GetComponentType() == itk::IOComponentEnum::FLOAT)
{
CompareMultiComponentImage<float>(testInput, validInput);
}
- else if (type.GetComponentType() == itk::ImageIOBase::DOUBLE)
+ else if (type.GetComponentType() == itk::IOComponentEnum::DOUBLE)
{
CompareMultiComponentImage<double>(testInput, validInput);
}
else
{
mitkThrow() << "Pixel component type not supported!";
}
}
template <typename TPixel>
void mitk::MultiComponentImageDataComparisonFilter::CompareMultiComponentImage(const Image *testImage,
const Image *validImage)
{
unsigned int noOfTimes = validImage->GetDimension(3);
unsigned int noOfPixels = validImage->GetDimension(0) * validImage->GetDimension(1) * validImage->GetDimension(2);
unsigned int noOfComponents = validImage->GetPixelType().GetNumberOfComponents();
for (unsigned int t = 0; t < noOfTimes; ++t)
{
ImageReadAccessor readAccTImage(testImage, testImage->GetVolumeData(t));
ImageReadAccessor readAccVImage(validImage, validImage->GetVolumeData(t));
for (unsigned int p = 0; p < noOfPixels * noOfComponents; ++p)
{
TPixel vDataItem = static_cast<TPixel *>(const_cast<void *>(readAccVImage.GetData()))[p];
TPixel tDataItem = static_cast<TPixel *>(const_cast<void *>(readAccTImage.GetData()))[p];
if (std::abs(static_cast<double>(tDataItem - vDataItem)) > m_Tolerance)
{
++m_CompareDetails->m_PixelsWithDifference;
m_CompareDetails->m_MaximumDifference =
std::max(m_CompareDetails->m_MaximumDifference, std::abs(static_cast<double>(tDataItem - vDataItem)));
double min =
std::min(m_CompareDetails->m_MinimumDifference, std::abs(static_cast<double>(tDataItem - vDataItem)));
if (min != 0.0f) // a difference of zero is not a difference!
m_CompareDetails->m_MinimumDifference = min;
m_CompareDetails->m_TotalDifference += std::abs(static_cast<double>(tDataItem - vDataItem));
}
}
}
if (m_CompareDetails->m_PixelsWithDifference > 0)
{
m_CompareDetails->m_MeanDifference =
m_CompareDetails->m_TotalDifference / m_CompareDetails->m_PixelsWithDifference;
m_CompareResult = false;
}
else
{
m_CompareResult = true;
}
m_CompareDetails->m_FilterCompleted = true;
}
} // end namespace mitk
diff --git a/Modules/Core/src/Algorithms/mitkRGBToRGBACastImageFilter.cpp b/Modules/Core/src/Algorithms/mitkRGBToRGBACastImageFilter.cpp
index 2ebeffa059..86fe47f153 100644
--- a/Modules/Core/src/Algorithms/mitkRGBToRGBACastImageFilter.cpp
+++ b/Modules/Core/src/Algorithms/mitkRGBToRGBACastImageFilter.cpp
@@ -1,211 +1,211 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkRGBToRGBACastImageFilter.h"
#include "mitkImageAccessByItk.h"
#include "mitkImageTimeSelector.h"
#include "mitkImageToItk.h"
#include "mitkProperties.h"
#include <itkImageIOBase.h>
#include <itkImageRegionConstIterator.h>
#include <itkImageRegionIteratorWithIndex.h>
#include <itkRGBAPixel.h>
mitk::RGBToRGBACastImageFilter::RGBToRGBACastImageFilter()
{
this->SetNumberOfIndexedInputs(1);
this->SetNumberOfRequiredInputs(1);
m_InputTimeSelector = mitk::ImageTimeSelector::New();
m_OutputTimeSelector = mitk::ImageTimeSelector::New();
}
mitk::RGBToRGBACastImageFilter::~RGBToRGBACastImageFilter()
{
}
bool mitk::RGBToRGBACastImageFilter::IsRGBImage(const mitk::Image *image)
{
const mitk::PixelType &inputPixelType = image->GetPixelType();
- if ((inputPixelType.GetPixelType() == itk::ImageIOBase::RGB) &&
- ((inputPixelType.GetComponentType() == itk::ImageIOBase::UCHAR) ||
- (inputPixelType.GetComponentType() == itk::ImageIOBase::USHORT) ||
- (inputPixelType.GetComponentType() == itk::ImageIOBase::FLOAT) ||
- (inputPixelType.GetComponentType() == itk::ImageIOBase::DOUBLE)))
+ if ((inputPixelType.GetPixelType() == itk::IOPixelEnum::RGB) &&
+ ((inputPixelType.GetComponentType() == itk::IOComponentEnum::UCHAR) ||
+ (inputPixelType.GetComponentType() == itk::IOComponentEnum::USHORT) ||
+ (inputPixelType.GetComponentType() == itk::IOComponentEnum::FLOAT) ||
+ (inputPixelType.GetComponentType() == itk::IOComponentEnum::DOUBLE)))
{
return true;
}
return false;
}
void mitk::RGBToRGBACastImageFilter::GenerateInputRequestedRegion()
{
Superclass::GenerateInputRequestedRegion();
mitk::Image *output = this->GetOutput();
mitk::Image *input = this->GetInput();
if (!output->IsInitialized())
{
return;
}
input->SetRequestedRegionToLargestPossibleRegion();
// GenerateTimeInInputRegion(output, input);
}
void mitk::RGBToRGBACastImageFilter::GenerateOutputInformation()
{
mitk::Image::ConstPointer input = this->GetInput();
mitk::Image::Pointer output = this->GetOutput();
if ((output->IsInitialized()) && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime()))
return;
itkDebugMacro(<< "GenerateOutputInformation()");
// Initialize RGBA output with same pixel type as input image
const mitk::PixelType &inputPixelType = input->GetPixelType();
typedef itk::Image<UCRGBPixelType> UCRGBItkImageType;
typedef itk::Image<USRGBPixelType> USRGBItkImageType;
typedef itk::Image<FloatRGBPixelType> FloatCRGBItkImageType;
typedef itk::Image<DoubleRGBPixelType> DoubleRGBItkImageType;
if (inputPixelType == mitk::MakePixelType<UCRGBItkImageType>())
{
const mitk::PixelType refPtype = MakePixelType<UCRGBItkImageType>();
output->Initialize(refPtype, *input->GetTimeGeometry());
}
else if (inputPixelType == mitk::MakePixelType<USRGBItkImageType>())
{
const mitk::PixelType refPtype = MakePixelType<USRGBItkImageType>();
output->Initialize(refPtype, *input->GetTimeGeometry());
}
else if (inputPixelType == mitk::MakePixelType<FloatCRGBItkImageType>())
{
const mitk::PixelType refPtype = MakePixelType<FloatCRGBItkImageType>();
output->Initialize(refPtype, *input->GetTimeGeometry());
}
else if (inputPixelType == mitk::MakePixelType<DoubleRGBItkImageType>())
{
const mitk::PixelType refPtype = MakePixelType<DoubleRGBItkImageType>();
output->Initialize(refPtype, *input->GetTimeGeometry());
}
output->SetPropertyList(input->GetPropertyList()->Clone());
m_TimeOfHeaderInitialization.Modified();
}
void mitk::RGBToRGBACastImageFilter::GenerateData()
{
mitk::Image::ConstPointer input = this->GetInput();
mitk::Image::Pointer output = this->GetOutput();
if (!output->IsInitialized())
{
return;
}
m_InputTimeSelector->SetInput(input);
m_OutputTimeSelector->SetInput(this->GetOutput());
mitk::Image::RegionType outputRegion = output->GetRequestedRegion();
const mitk::TimeGeometry *outputTimeGeometry = output->GetTimeGeometry();
const mitk::TimeGeometry *inputTimeGeometry = input->GetTimeGeometry();
TimePointType timeInMS;
int timestep = 0;
int tstart = outputRegion.GetIndex(3);
int tmax = tstart + outputRegion.GetSize(3);
int t;
for (t = tstart; t < tmax; ++t)
{
timeInMS = outputTimeGeometry->TimeStepToTimePoint(t);
timestep = inputTimeGeometry->TimePointToTimeStep(timeInMS);
m_InputTimeSelector->SetTimeNr(timestep);
m_InputTimeSelector->UpdateLargestPossibleRegion();
m_OutputTimeSelector->SetTimeNr(t);
m_OutputTimeSelector->UpdateLargestPossibleRegion();
mitk::Image *image = m_InputTimeSelector->GetOutput();
const mitk::PixelType &pixelType = image->GetPixelType();
// Check if the pixel type is supported
if (pixelType == MakePixelType<itk::Image<UCRGBPixelType>>())
{
AccessFixedPixelTypeByItk_2(image, InternalCast, (UCRGBPixelType), this, 255);
}
else if (pixelType == MakePixelType<itk::Image<USRGBPixelType>>())
{
AccessFixedPixelTypeByItk_2(image, InternalCast, (USRGBPixelType), this, 65535);
}
else if (pixelType == MakePixelType<itk::Image<FloatRGBPixelType>>())
{
AccessFixedPixelTypeByItk_2(image, InternalCast, (FloatRGBPixelType), this, 1.0);
}
else if (pixelType == MakePixelType<itk::Image<DoubleRGBPixelType>>())
{
AccessFixedPixelTypeByItk_2(image, InternalCast, (DoubleRGBPixelType), this, 1.0);
}
else
{
// Otherwise, write warning and graft input to output
// ...TBD...
}
}
m_TimeOfHeaderInitialization.Modified();
}
template <typename TPixel, unsigned int VImageDimension>
void mitk::RGBToRGBACastImageFilter::InternalCast(itk::Image<TPixel, VImageDimension> *inputItkImage,
mitk::RGBToRGBACastImageFilter *addComponentFilter,
typename TPixel::ComponentType defaultAlpha)
{
typedef TPixel InputPixelType;
typedef itk::RGBAPixel<typename TPixel::ComponentType> OutputPixelType;
typedef itk::Image<InputPixelType, VImageDimension> InputImageType;
typedef itk::Image<OutputPixelType, VImageDimension> OutputImageType;
typedef itk::ImageRegionConstIterator<InputImageType> InputImageIteratorType;
typedef itk::ImageRegionIteratorWithIndex<OutputImageType> OutputImageIteratorType;
typename mitk::ImageToItk<OutputImageType>::Pointer outputimagetoitk = mitk::ImageToItk<OutputImageType>::New();
outputimagetoitk->SetInput(addComponentFilter->m_OutputTimeSelector->GetOutput());
outputimagetoitk->Update();
typename OutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput();
// create the iterators
typename InputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion();
InputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest);
OutputImageIteratorType outputIt(outputItkImage, inputRegionOfInterest);
for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt)
{
typename InputPixelType::Iterator pixelInputIt = inputIt.Get().Begin();
typename OutputPixelType::Iterator pixelOutputIt = outputIt.Get().Begin();
*pixelOutputIt++ = *pixelInputIt++;
*pixelOutputIt++ = *pixelInputIt++;
*pixelOutputIt++ = *pixelInputIt++;
*pixelOutputIt = defaultAlpha;
}
}
diff --git a/Modules/Core/src/Controllers/mitkLimitedLinearUndo.cpp b/Modules/Core/src/Controllers/mitkLimitedLinearUndo.cpp
index 08d7f3dae4..7f69c6ee80 100644
--- a/Modules/Core/src/Controllers/mitkLimitedLinearUndo.cpp
+++ b/Modules/Core/src/Controllers/mitkLimitedLinearUndo.cpp
@@ -1,236 +1,247 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkLimitedLinearUndo.h"
#include <mitkRenderingManager.h>
+namespace mitk
+{
+ itkEventMacroDefinition(UndoStackEvent, itk::ModifiedEvent);
+ itkEventMacroDefinition(UndoEmptyEvent, UndoStackEvent);
+ itkEventMacroDefinition(RedoEmptyEvent, UndoStackEvent);
+ itkEventMacroDefinition(UndoNotEmptyEvent, UndoStackEvent);
+ itkEventMacroDefinition(RedoNotEmptyEvent, UndoStackEvent);
+ itkEventMacroDefinition(UndoFullEvent, UndoStackEvent);
+ itkEventMacroDefinition(RedoFullEvent, UndoStackEvent);
+}
+
mitk::LimitedLinearUndo::LimitedLinearUndo()
: m_UndoLimit(0)
{
// nothing to do
}
mitk::LimitedLinearUndo::~LimitedLinearUndo()
{
// delete undo and redo list
this->ClearList(&m_UndoList);
this->ClearList(&m_RedoList);
}
void mitk::LimitedLinearUndo::ClearList(UndoContainer *list)
{
while (!list->empty())
{
UndoStackItem *item = list->back();
list->pop_back();
delete item;
}
}
bool mitk::LimitedLinearUndo::SetOperationEvent(UndoStackItem *stackItem)
{
auto *operationEvent = dynamic_cast<OperationEvent *>(stackItem);
if (!operationEvent)
return false;
// clear the redolist, if a new operation is saved
if (!m_RedoList.empty())
{
this->ClearList(&m_RedoList);
InvokeEvent(RedoEmptyEvent());
}
if (0 != m_UndoLimit && m_UndoList.size() == m_UndoLimit)
{
auto item = m_UndoList.front();
m_UndoList.pop_front();
delete item;
}
m_UndoList.push_back(operationEvent);
InvokeEvent(UndoNotEmptyEvent());
return true;
}
bool mitk::LimitedLinearUndo::Undo(bool fine)
{
if (fine)
{
// undo one object event ID
return Undo();
}
else
{
// undo one group event ID
int oeid = FirstObjectEventIdOfCurrentGroup(
m_UndoList); // get the Object Event ID of the first item with a differnt Group ID (as seen from the end of stack)
return Undo(oeid);
}
}
bool mitk::LimitedLinearUndo::Undo()
{
if (m_UndoList.empty())
return false;
int undoObjectEventId = m_UndoList.back()->GetObjectEventId();
return Undo(undoObjectEventId);
}
bool mitk::LimitedLinearUndo::Undo(int oeid)
{
if (m_UndoList.empty())
return false;
bool rc = true;
do
{
m_UndoList.back()->ReverseAndExecute();
m_RedoList.push_back(m_UndoList.back()); // move to redo stack
m_UndoList.pop_back();
InvokeEvent(RedoNotEmptyEvent());
if (m_UndoList.empty())
{
InvokeEvent(UndoEmptyEvent());
rc = false;
break;
}
} while (m_UndoList.back()->GetObjectEventId() >= oeid);
// Update. Check Rendering Mechanism where to request updates
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
return rc;
}
bool mitk::LimitedLinearUndo::Redo(bool)
{
return Redo();
}
bool mitk::LimitedLinearUndo::Redo()
{
if (m_RedoList.empty())
return false;
int redoObjectEventId = m_RedoList.back()->GetObjectEventId();
return Redo(redoObjectEventId);
}
bool mitk::LimitedLinearUndo::Redo(int oeid)
{
if (m_RedoList.empty())
return false;
do
{
m_RedoList.back()->ReverseAndExecute();
m_UndoList.push_back(m_RedoList.back());
m_RedoList.pop_back();
InvokeEvent(UndoNotEmptyEvent());
if (m_RedoList.empty())
{
InvokeEvent(RedoEmptyEvent());
break;
}
} while (m_RedoList.back()->GetObjectEventId() <= oeid);
// Update. This should belong into the ExecuteOperation() of OperationActors, but it seems not to be used everywhere
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
return true;
}
void mitk::LimitedLinearUndo::Clear()
{
this->ClearList(&m_UndoList);
InvokeEvent(UndoEmptyEvent());
this->ClearList(&m_RedoList);
InvokeEvent(RedoEmptyEvent());
}
void mitk::LimitedLinearUndo::ClearRedoList()
{
this->ClearList(&m_RedoList);
InvokeEvent(RedoEmptyEvent());
}
bool mitk::LimitedLinearUndo::RedoListEmpty()
{
return m_RedoList.empty();
}
std::size_t mitk::LimitedLinearUndo::GetUndoLimit() const
{
return m_UndoLimit;
}
void mitk::LimitedLinearUndo::SetUndoLimit(std::size_t undoLimit)
{
if (undoLimit != m_UndoLimit)
{
if (m_UndoList.size() > undoLimit)
{
m_UndoList.erase(m_UndoList.begin(), m_UndoList.end() - undoLimit);
}
m_UndoLimit = undoLimit;
}
}
int mitk::LimitedLinearUndo::GetLastObjectEventIdInList()
{
return m_UndoList.back()->GetObjectEventId();
}
int mitk::LimitedLinearUndo::GetLastGroupEventIdInList()
{
return m_UndoList.back()->GetGroupEventId();
}
mitk::OperationEvent *mitk::LimitedLinearUndo::GetLastOfType(OperationActor *destination, OperationType opType)
{
// When/where is this function needed? In CoordinateSupplier...
for (auto iter = m_UndoList.rbegin(); iter != m_UndoList.rend(); ++iter)
{
auto *opEvent = dynamic_cast<OperationEvent *>(*iter);
if (!opEvent)
continue;
if (opEvent->GetOperation() != nullptr && opEvent->GetOperation()->GetOperationType() == opType &&
opEvent->IsValid() && opEvent->GetDestination() == destination)
return opEvent;
}
return nullptr;
}
int mitk::LimitedLinearUndo::FirstObjectEventIdOfCurrentGroup(mitk::LimitedLinearUndo::UndoContainer &stack)
{
int currentGroupEventId = stack.back()->GetGroupEventId();
int firstObjectEventId = -1;
for (auto iter = stack.rbegin(); iter != stack.rend(); ++iter)
{
if ((*iter)->GetGroupEventId() == currentGroupEventId)
{
firstObjectEventId = (*iter)->GetObjectEventId();
}
else
break;
}
return firstObjectEventId;
}
diff --git a/Modules/Core/src/Controllers/mitkRenderingManager.cpp b/Modules/Core/src/Controllers/mitkRenderingManager.cpp
index eb09085e41..db64173826 100644
--- a/Modules/Core/src/Controllers/mitkRenderingManager.cpp
+++ b/Modules/Core/src/Controllers/mitkRenderingManager.cpp
@@ -1,772 +1,770 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <mitkRenderingManager.h>
#include <mitkRenderingManagerFactory.h>
#include <mitkBaseRenderer.h>
#include <mitkCameraController.h>
#include <mitkNodePredicateNot.h>
#include <mitkNodePredicateProperty.h>
#include <mitkProportionalTimeGeometry.h>
#include <vtkCamera.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkRendererCollection.h>
#include <itkCommand.h>
#include <mitkVtkPropRenderer.h>
namespace mitk
{
+ itkEventMacroDefinition(RenderingManagerEvent, itk::AnyEvent);
+ itkEventMacroDefinition(RenderingManagerViewsInitializedEvent, RenderingManagerEvent);
itkEventMacroDefinition(FocusChangedEvent, itk::AnyEvent);
RenderingManager::Pointer RenderingManager::s_Instance = nullptr;
RenderingManagerFactory *RenderingManager::s_RenderingManagerFactory = nullptr;
RenderingManager::RenderingManager()
: m_UpdatePending(false),
m_MaxLOD(1),
m_LODIncreaseBlocked(false),
m_LODAbortMechanismEnabled(false),
m_ClippingPlaneEnabled(false),
m_TimeNavigationController(SliceNavigationController::New()),
m_DataStorage(nullptr),
m_ConstrainedPanningZooming(true),
m_FocusedRenderWindow(nullptr),
m_AntiAliasing(AntiAliasing::FastApproximate)
{
m_ShadingEnabled.assign(3, false);
m_ShadingValues.assign(4, 0.0);
InitializePropertyList();
}
RenderingManager::~RenderingManager()
{
// Decrease reference counts of all registered vtkRenderWindows for
// proper destruction
RenderWindowVector::iterator it;
for (it = m_AllRenderWindows.begin(); it != m_AllRenderWindows.end(); ++it)
{
(*it)->UnRegister(nullptr);
auto callbacks_it = this->m_RenderWindowCallbacksList.find(*it);
if (callbacks_it != this->m_RenderWindowCallbacksList.end())
{
(*it)->RemoveObserver(callbacks_it->second.commands[0u]);
(*it)->RemoveObserver(callbacks_it->second.commands[1u]);
(*it)->RemoveObserver(callbacks_it->second.commands[2u]);
}
}
}
void RenderingManager::SetFactory(RenderingManagerFactory *factory) { s_RenderingManagerFactory = factory; }
const RenderingManagerFactory *RenderingManager::GetFactory() { return s_RenderingManagerFactory; }
bool RenderingManager::HasFactory()
{
if (RenderingManager::s_RenderingManagerFactory)
{
return true;
}
else
{
return false;
}
}
RenderingManager::Pointer RenderingManager::New()
{
const RenderingManagerFactory *factory = GetFactory();
if (factory == nullptr)
return nullptr;
return factory->CreateRenderingManager();
}
RenderingManager *RenderingManager::GetInstance()
{
if (!RenderingManager::s_Instance)
{
if (s_RenderingManagerFactory)
{
s_Instance = s_RenderingManagerFactory->CreateRenderingManager();
}
}
return s_Instance;
}
bool RenderingManager::IsInstantiated()
{
if (RenderingManager::s_Instance)
return true;
else
return false;
}
void RenderingManager::AddRenderWindow(vtkRenderWindow *renderWindow)
{
if (renderWindow && (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.end()))
{
m_RenderWindowList[renderWindow] = RENDERING_INACTIVE;
m_AllRenderWindows.push_back(renderWindow);
if (m_DataStorage.IsNotNull())
BaseRenderer::GetInstance(renderWindow)->SetDataStorage(m_DataStorage.GetPointer());
// Register vtkRenderWindow instance
renderWindow->Register(nullptr);
// Add callbacks for rendering abort mechanism
// BaseRenderer *renderer = BaseRenderer::GetInstance( renderWindow );
vtkCallbackCommand *startCallbackCommand = vtkCallbackCommand::New();
startCallbackCommand->SetCallback(RenderingManager::RenderingStartCallback);
renderWindow->AddObserver(vtkCommand::StartEvent, startCallbackCommand);
vtkCallbackCommand *progressCallbackCommand = vtkCallbackCommand::New();
progressCallbackCommand->SetCallback(RenderingManager::RenderingProgressCallback);
renderWindow->AddObserver(vtkCommand::AbortCheckEvent, progressCallbackCommand);
vtkCallbackCommand *endCallbackCommand = vtkCallbackCommand::New();
endCallbackCommand->SetCallback(RenderingManager::RenderingEndCallback);
renderWindow->AddObserver(vtkCommand::EndEvent, endCallbackCommand);
RenderWindowCallbacks callbacks;
callbacks.commands[0u] = startCallbackCommand;
callbacks.commands[1u] = progressCallbackCommand;
callbacks.commands[2u] = endCallbackCommand;
this->m_RenderWindowCallbacksList[renderWindow] = callbacks;
// Delete vtk variables correctly
startCallbackCommand->Delete();
progressCallbackCommand->Delete();
endCallbackCommand->Delete();
}
}
void RenderingManager::RemoveRenderWindow(vtkRenderWindow *renderWindow)
{
if (m_RenderWindowList.erase(renderWindow))
{
auto callbacks_it = this->m_RenderWindowCallbacksList.find(renderWindow);
if (callbacks_it != this->m_RenderWindowCallbacksList.end())
{
renderWindow->RemoveObserver(callbacks_it->second.commands[0u]);
renderWindow->RemoveObserver(callbacks_it->second.commands[1u]);
renderWindow->RemoveObserver(callbacks_it->second.commands[2u]);
this->m_RenderWindowCallbacksList.erase(callbacks_it);
}
auto rw_it =
std::find(m_AllRenderWindows.begin(), m_AllRenderWindows.end(), renderWindow);
if (rw_it != m_AllRenderWindows.cend())
{
// Decrease reference count for proper destruction
(*rw_it)->UnRegister(nullptr);
m_AllRenderWindows.erase(rw_it);
}
}
}
const RenderingManager::RenderWindowVector &RenderingManager::GetAllRegisteredRenderWindows()
{
return m_AllRenderWindows;
}
void RenderingManager::RequestUpdate(vtkRenderWindow *renderWindow)
{
// If the renderWindow is not valid, we do not want to inadvertantly create
// an entry in the m_RenderWindowList map. It is possible if the user is
// regularly calling AddRenderer and RemoveRenderer for a rendering update
// to come into this method with a renderWindow pointer that is valid in the
// sense that the window does exist within the application, but that
// renderWindow has been temporarily removed from this RenderingManager for
// performance reasons.
if (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.cend())
{
return;
}
m_RenderWindowList[renderWindow] = RENDERING_REQUESTED;
if (!m_UpdatePending)
{
m_UpdatePending = true;
this->GenerateRenderingRequestEvent();
}
}
void RenderingManager::ForceImmediateUpdate(vtkRenderWindow *renderWindow)
{
// If the renderWindow is not valid, we do not want to inadvertantly create
// an entry in the m_RenderWindowList map. It is possible if the user is
// regularly calling AddRenderer and RemoveRenderer for a rendering update
// to come into this method with a renderWindow pointer that is valid in the
// sense that the window does exist within the application, but that
// renderWindow has been temporarily removed from this RenderingManager for
// performance reasons.
if (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.cend())
{
return;
}
// Erase potentially pending requests for this window
m_RenderWindowList[renderWindow] = RENDERING_INACTIVE;
m_UpdatePending = false;
// Immediately repaint this window (implementation platform specific)
// If the size is 0 it crashes
int *size = renderWindow->GetSize();
if (0 != size[0] && 0 != size[1])
{
// prepare the camera etc. before rendering
// Note: this is a very important step which should be called before the VTK render!
// If you modify the camera anywhere else or after the render call, the scene cannot be seen.
auto *vPR = dynamic_cast<VtkPropRenderer *>(BaseRenderer::GetInstance(renderWindow));
if (vPR)
vPR->PrepareRender();
// Execute rendering
renderWindow->Render();
}
}
void RenderingManager::RequestUpdateAll(RequestType type)
{
RenderWindowList::const_iterator it;
for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it)
{
int id = BaseRenderer::GetInstance(it->first)->GetMapperID();
if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) ||
((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2)))
{
this->RequestUpdate(it->first);
}
}
}
void RenderingManager::ForceImmediateUpdateAll(RequestType type)
{
RenderWindowList::const_iterator it;
for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it)
{
int id = BaseRenderer::GetInstance(it->first)->GetMapperID();
if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) ||
((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2)))
{
// Immediately repaint this window (implementation platform specific)
// If the size is 0, it crashes
this->ForceImmediateUpdate(it->first);
}
}
}
void RenderingManager::InitializeViewsByBoundingObjects(const DataStorage* dataStorage)
{
if (nullptr == dataStorage)
{
return;
}
// get all nodes that have not set "includeInBoundingBox" to false
auto pred = NodePredicateNot::New(NodePredicateProperty::New("includeInBoundingBox", BoolProperty::New(false)));
DataStorage::SetOfObjects::ConstPointer filteredNodes = dataStorage->GetSubset(pred);
TimeGeometry::ConstPointer boundingGeometry;
if (!filteredNodes->empty())
{
// calculate bounding geometry of these nodes
boundingGeometry = dataStorage->ComputeBoundingGeometry3D(filteredNodes, "visible");
}
// initialize the views to the bounding geometry
this->InitializeViews(boundingGeometry);
}
- bool RenderingManager::InitializeViews(const BaseGeometry *geometry, RequestType type,
- bool resetCamera)
+ bool RenderingManager::InitializeViews(const BaseGeometry* geometry, RequestType type, bool resetCamera)
{
ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New();
propTimeGeometry->Initialize(dynamic_cast<BaseGeometry *>(geometry->Clone().GetPointer()), 1);
return this->InitializeViews(propTimeGeometry, type, resetCamera);
}
- bool RenderingManager::InitializeViews(const TimeGeometry *geometry, RequestType type,
- bool resetCamera)
+ bool RenderingManager::InitializeViews(const TimeGeometry* geometry, RequestType type, bool resetCamera)
{
bool boundingBoxInitialized = false;
TimeGeometry::Pointer modifiedGeometry = nullptr;
try
{
boundingBoxInitialized = this->ExtendGeometryForBoundingBox(geometry, modifiedGeometry);
}
catch (Exception& exception)
{
mitkReThrow(exception);
}
RenderWindowVector allRenderWindows = this->GetAllRegisteredRenderWindows();
RenderWindowVector::const_iterator it;
for (it = allRenderWindows.cbegin(); it != allRenderWindows.cend(); ++it)
{
BaseRenderer *baseRenderer = BaseRenderer::GetInstance(*it);
baseRenderer->SetConstrainZoomingAndPanning(this->GetConstrainedPanningZooming());
int id = baseRenderer->GetMapperID();
if ((type == REQUEST_UPDATE_ALL) ||
((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) ||
((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2)))
{
this->InternalViewInitialization(baseRenderer, modifiedGeometry, boundingBoxInitialized, id, resetCamera);
}
}
if (boundingBoxInitialized)
{
this->GetTimeNavigationController()->SetInputWorldTimeGeometry(modifiedGeometry);
}
this->GetTimeNavigationController()->Update();
this->RequestUpdateAll(type);
// inform listeners that views have been initialized
this->InvokeEvent(RenderingManagerViewsInitializedEvent());
return boundingBoxInitialized;
}
bool RenderingManager::InitializeViews(RequestType type)
{
const RenderWindowVector allRenderWindows = this->GetAllRegisteredRenderWindows();
RenderWindowVector::const_iterator it;
for (it = allRenderWindows.cbegin(); it != allRenderWindows.cend(); ++it)
{
BaseRenderer *baseRenderer = BaseRenderer::GetInstance(*it);
int id = baseRenderer->GetMapperID();
if ((type == REQUEST_UPDATE_ALL) ||
((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) ||
((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2)))
{
this->InternalViewInitialization(baseRenderer, nullptr, false, id, false);
}
}
this->RequestUpdateAll(type);
// inform listeners that views have been initialized
this->InvokeEvent(RenderingManagerViewsInitializedEvent());
return true;
}
- bool RenderingManager::InitializeView(vtkRenderWindow *renderWindow, const BaseGeometry *geometry,
- bool initializeGlobalTime, bool resetCamera)
+ bool RenderingManager::InitializeView(vtkRenderWindow* renderWindow, const BaseGeometry* geometry, bool resetCamera)
{
ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New();
- propTimeGeometry->Initialize(dynamic_cast<BaseGeometry *>(geometry->Clone().GetPointer()), 1);
- return this->InitializeView(renderWindow, propTimeGeometry, initializeGlobalTime, resetCamera);
+ propTimeGeometry->Initialize(dynamic_cast<BaseGeometry*>(geometry->Clone().GetPointer()), 1);
+ return this->InitializeView(renderWindow, propTimeGeometry, resetCamera);
}
- bool RenderingManager::InitializeView(vtkRenderWindow *renderWindow, const TimeGeometry *geometry,
- bool initializeGlobalTime, bool resetCamera)
+ bool RenderingManager::InitializeView(vtkRenderWindow* renderWindow, const TimeGeometry* geometry, bool resetCamera)
{
bool boundingBoxInitialized = false;
TimeGeometry::Pointer modifiedGeometry = nullptr;
try
{
boundingBoxInitialized = this->ExtendGeometryForBoundingBox(geometry, modifiedGeometry);
}
catch (Exception &exception)
{
mitkReThrow(exception);
}
- BaseRenderer *baseRenderer = BaseRenderer::GetInstance(renderWindow);
+ BaseRenderer* baseRenderer = BaseRenderer::GetInstance(renderWindow);
baseRenderer->SetConstrainZoomingAndPanning(this->GetConstrainedPanningZooming());
int id = baseRenderer->GetMapperID();
this->InternalViewInitialization(baseRenderer, modifiedGeometry, boundingBoxInitialized, id, resetCamera);
- if (boundingBoxInitialized && initializeGlobalTime)
+ if (boundingBoxInitialized)
{
this->GetTimeNavigationController()->SetInputWorldTimeGeometry(modifiedGeometry);
}
this->GetTimeNavigationController()->Update();
this->RequestUpdate(renderWindow);
// inform listeners that views have been initialized
this->InvokeEvent(RenderingManagerViewsInitializedEvent());
return boundingBoxInitialized;
}
bool RenderingManager::InitializeView(vtkRenderWindow *renderWindow)
{
BaseRenderer *baseRenderer = BaseRenderer::GetInstance(renderWindow);
int id = baseRenderer->GetMapperID();
this->InternalViewInitialization(baseRenderer, nullptr, false, id, false);
this->RequestUpdate(renderWindow);
// inform listeners that views have been initialized
this->InvokeEvent(RenderingManagerViewsInitializedEvent());
return true;
}
void RenderingManager::InternalViewInitialization(BaseRenderer *baseRenderer, const TimeGeometry *geometry,
bool boundingBoxInitialized, int mapperID, bool resetCamera)
{
SliceNavigationController *nc = baseRenderer->GetSliceNavigationController();
// Re-initialize view direction
nc->SetViewDirectionToDefault();
if (boundingBoxInitialized)
{
// Set geometry for NC
nc->SetInputWorldTimeGeometry(geometry);
nc->Update();
if (resetCamera)
{
if (mapperID == BaseRenderer::Standard2D)
{
// For 2D SNCs, steppers are set so that the cross is centered in the image
nc->GetSlice()->SetPos(nc->GetSlice()->GetSteps() / 2);
baseRenderer->GetCameraController()->Fit();
}
else if (mapperID == BaseRenderer::Standard3D)
{
baseRenderer->GetCameraController()->SetViewToAnterior();
}
}
}
else
{
nc->Update();
}
}
bool RenderingManager::ExtendGeometryForBoundingBox(const TimeGeometry *geometry,
TimeGeometry::Pointer& modifiedGeometry)
{
bool boundingBoxInitialized = false;
if (nullptr == geometry)
{
return boundingBoxInitialized;
}
modifiedGeometry = geometry->Clone();
if (modifiedGeometry.IsNull())
{
return boundingBoxInitialized;
}
if (modifiedGeometry->GetBoundingBoxInWorld()->GetDiagonalLength2() > eps)
{
boundingBoxInitialized = true;
}
// make sure bounding box has an extent bigger than zero in any direction
for (TimeStepType step = 0; step < modifiedGeometry->CountTimeSteps(); ++step)
{
BaseGeometry::BoundsArrayType newBounds = modifiedGeometry->GetGeometryForTimeStep(step)->GetBounds();
for (unsigned int dimension = 0; (2 * dimension) < newBounds.Size(); dimension++)
{
// check for equality but for an epsilon
if (Equal(newBounds[2 * dimension], newBounds[2 * dimension + 1]))
{
newBounds[2 * dimension + 1] += 1;
if (Equal(
newBounds[2 * dimension],
newBounds[2 * dimension + 1])) // newBounds will still be equal if values are beyond double precision
{
mitkThrow() << "One dimension of object data has zero length, please make sure you're not using numbers "
"beyond double precision as coordinates.";
}
}
}
modifiedGeometry->GetGeometryForTimeStep(step)->SetBounds(newBounds);
}
return boundingBoxInitialized;
}
const SliceNavigationController *RenderingManager::GetTimeNavigationController() const
{
return m_TimeNavigationController.GetPointer();
}
SliceNavigationController *RenderingManager::GetTimeNavigationController()
{
return m_TimeNavigationController.GetPointer();
}
void RenderingManager::ExecutePendingRequests()
{
m_UpdatePending = false;
// Satisfy all pending update requests
RenderWindowList::const_iterator it;
int i = 0;
for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it, ++i)
{
if (it->second == RENDERING_REQUESTED)
{
this->ForceImmediateUpdate(it->first);
}
}
}
void RenderingManager::RenderingStartCallback(vtkObject *caller, unsigned long, void *, void *)
{
auto renderingManager = RenderingManager::GetInstance();
auto renderWindow = dynamic_cast<vtkRenderWindow*>(caller);
if (nullptr != renderWindow)
renderingManager->m_RenderWindowList[renderWindow] = RENDERING_INPROGRESS;
renderingManager->m_UpdatePending = false;
}
void RenderingManager::RenderingProgressCallback(vtkObject *caller, unsigned long, void *, void *)
{
auto renderingManager = RenderingManager::GetInstance();
if (renderingManager->m_LODAbortMechanismEnabled)
{
auto renderWindow = dynamic_cast<vtkRenderWindow *>(caller);
if (nullptr != renderWindow)
{
auto renderer = BaseRenderer::GetInstance(renderWindow);
if (nullptr != renderer && 0 < renderer->GetNumberOfVisibleLODEnabledMappers())
renderingManager->DoMonitorRendering();
}
}
}
void RenderingManager::RenderingEndCallback(vtkObject *caller, unsigned long, void *, void *)
{
auto renderWindow = dynamic_cast<vtkRenderWindow*>(caller);
if (nullptr == renderWindow)
{
return;
}
auto renderer = BaseRenderer::GetInstance(renderWindow);
if (nullptr == renderer)
{
return;
}
auto renderingManager = RenderingManager::GetInstance();
renderingManager->m_RenderWindowList[renderer->GetRenderWindow()] = RENDERING_INACTIVE;
if (0 < renderer->GetNumberOfVisibleLODEnabledMappers())
{
if (0 == renderingManager->m_NextLODMap[renderer])
{
renderingManager->StartOrResetTimer();
}
else
{
renderingManager->m_NextLODMap[renderer] = 0;
}
}
}
bool RenderingManager::IsRendering() const
{
RenderWindowList::const_iterator it;
for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it)
{
if (it->second == RENDERING_INPROGRESS)
{
return true;
}
}
return false;
}
void RenderingManager::AbortRendering()
{
RenderWindowList::const_iterator it;
for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it)
{
if (it->second == RENDERING_INPROGRESS)
{
it->first->SetAbortRender(true);
m_RenderingAbortedMap[BaseRenderer::GetInstance(it->first)] = true;
}
}
}
int RenderingManager::GetNextLOD(BaseRenderer *renderer)
{
if (renderer != nullptr)
{
return m_NextLODMap[renderer];
}
else
{
return 0;
}
}
void RenderingManager::ExecutePendingHighResRenderingRequest()
{
RenderWindowList::const_iterator it;
for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it)
{
BaseRenderer *renderer = BaseRenderer::GetInstance(it->first);
if (renderer->GetNumberOfVisibleLODEnabledMappers() > 0)
{
if (m_NextLODMap[renderer] == 0)
{
m_NextLODMap[renderer] = 1;
RequestUpdate(it->first);
}
}
}
}
void RenderingManager::SetMaximumLOD(unsigned int max) { m_MaxLOD = max; }
// enable/disable shading
void RenderingManager::SetShading(bool state, unsigned int lod)
{
if (lod > m_MaxLOD)
{
itkWarningMacro(<< "LOD out of range requested: " << lod << " maxLOD: " << m_MaxLOD);
return;
}
m_ShadingEnabled[lod] = state;
}
bool RenderingManager::GetShading(unsigned int lod)
{
if (lod > m_MaxLOD)
{
itkWarningMacro(<< "LOD out of range requested: " << lod << " maxLOD: " << m_MaxLOD);
return false;
}
return m_ShadingEnabled[lod];
}
// enable/disable the clipping plane
void RenderingManager::SetClippingPlaneStatus(bool status) { m_ClippingPlaneEnabled = status; }
bool RenderingManager::GetClippingPlaneStatus() { return m_ClippingPlaneEnabled; }
void RenderingManager::SetShadingValues(float ambient, float diffuse, float specular, float specpower)
{
m_ShadingValues[0] = ambient;
m_ShadingValues[1] = diffuse;
m_ShadingValues[2] = specular;
m_ShadingValues[3] = specpower;
}
RenderingManager::FloatVector &RenderingManager::GetShadingValues() { return m_ShadingValues; }
void RenderingManager::InitializePropertyList()
{
if (m_PropertyList.IsNull())
{
m_PropertyList = PropertyList::New();
}
this->SetProperty("coupled-zoom", BoolProperty::New(false));
this->SetProperty("coupled-plane-rotation", BoolProperty::New(false));
this->SetProperty("MIP-slice-rendering", BoolProperty::New(false));
}
PropertyList::Pointer RenderingManager::GetPropertyList() const { return m_PropertyList; }
BaseProperty *RenderingManager::GetProperty(const char *propertyKey) const
{
return m_PropertyList->GetProperty(propertyKey);
}
void RenderingManager::SetProperty(const char *propertyKey, BaseProperty *propertyValue)
{
m_PropertyList->SetProperty(propertyKey, propertyValue);
}
void RenderingManager::SetDataStorage(DataStorage *storage)
{
if (storage != nullptr)
{
m_DataStorage = storage;
RenderingManager::RenderWindowVector::const_iterator iter;
for (iter = m_AllRenderWindows.cbegin(); iter < m_AllRenderWindows.cend(); ++iter)
{
BaseRenderer::GetInstance((*iter))->SetDataStorage(m_DataStorage.GetPointer());
}
}
}
void RenderingManager::SetRenderWindowFocus(vtkRenderWindow *focusWindow)
{
if (focusWindow != m_FocusedRenderWindow)
{
if (!focusWindow || (m_RenderWindowList.find(focusWindow) != m_RenderWindowList.cend()))
{
m_FocusedRenderWindow = focusWindow;
this->InvokeEvent(FocusChangedEvent());
return;
}
MITK_ERROR << "Tried to set a RenderWindow that does not exist.";
}
}
void RenderingManager::SetAntiAliasing(AntiAliasing antiAliasing)
{
if (m_AntiAliasing != antiAliasing)
{
auto renderingManager = RenderingManager::GetInstance();
auto renderWindows = renderingManager->GetAllRegisteredRenderWindows();
for (auto renderWindow : renderWindows)
{
auto renderers = renderWindow->GetRenderers();
if (nullptr != renderers)
{
renderers->InitTraversal();
auto renderer = renderers->GetNextItem();
while (nullptr != renderer)
{
renderer->SetUseFXAA(AntiAliasing::FastApproximate == antiAliasing);
renderer = renderers->GetNextItem();
}
renderingManager->RequestUpdate(renderWindow);
}
}
m_AntiAliasing = antiAliasing;
}
}
// Create and register generic RenderingManagerFactory.
TestingRenderingManagerFactory renderingManagerFactory;
} // namespace
diff --git a/Modules/Core/src/Controllers/mitkSlicesCoordinator.cpp b/Modules/Core/src/Controllers/mitkSlicesCoordinator.cpp
index db726896ce..be8f3d8c63 100644
--- a/Modules/Core/src/Controllers/mitkSlicesCoordinator.cpp
+++ b/Modules/Core/src/Controllers/mitkSlicesCoordinator.cpp
@@ -1,82 +1,84 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <mitkSlicesCoordinator.h>
#include <mitkApplicationCursor.h>
namespace mitk
{
+ itkEventMacroDefinition(SliceRotationEvent, itk::AnyEvent);
+
SlicesCoordinator::SlicesCoordinator() : m_LinkPlanes(true), m_MouseCursorSet(false) {}
SlicesCoordinator::~SlicesCoordinator() {}
void SlicesCoordinator::AddSliceController(SliceNavigationController *snc)
{
if (!snc)
return;
m_SliceNavigationControllers.push_back(snc);
OnSliceControllerAdded(snc); // notify
}
void SlicesCoordinator::RemoveSliceController(SliceNavigationController *snc)
{
if (!snc)
return;
// see, whether snc is in our list
SNCVector::iterator iter;
for (iter = m_SliceNavigationControllers.begin(); iter != m_SliceNavigationControllers.end(); ++iter)
if (*iter == snc)
break;
// if found, remove from list
if (iter != m_SliceNavigationControllers.end())
{
m_SliceNavigationControllers.erase(iter);
OnSliceControllerRemoved(snc);
}
}
void SlicesCoordinator::ResetMouseCursor()
{
if (m_MouseCursorSet)
{
ApplicationCursor::GetInstance()->PopCursor();
m_MouseCursorSet = false;
}
}
void SlicesCoordinator::SetMouseCursor(const char *xpm[], int hotspotX, int hotspotY)
{
// Remove previously set mouse cursor
if (m_MouseCursorSet)
{
ApplicationCursor::GetInstance()->PopCursor();
}
ApplicationCursor::GetInstance()->PushCursor(xpm, hotspotX, hotspotY);
m_MouseCursorSet = true;
}
void SlicesCoordinator::OnSliceControllerAdded(SliceNavigationController *)
{
// implement in subclasses
}
void SlicesCoordinator::OnSliceControllerRemoved(SliceNavigationController *)
{
// implement in subclasses
}
} // namespace
diff --git a/Modules/Core/src/DataManagement/mitkAbstractTransformGeometry.cpp b/Modules/Core/src/DataManagement/mitkAbstractTransformGeometry.cpp
index a859544e35..31eb0ac623 100644
--- a/Modules/Core/src/DataManagement/mitkAbstractTransformGeometry.cpp
+++ b/Modules/Core/src/DataManagement/mitkAbstractTransformGeometry.cpp
@@ -1,330 +1,332 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkAbstractTransformGeometry.h"
#include <vtkAbstractTransform.h>
mitk::AbstractTransformGeometry::AbstractTransformGeometry() : Superclass(), m_Plane(nullptr), m_FrameGeometry(nullptr)
{
Initialize();
m_ItkVtkAbstractTransform = itk::VtkAbstractTransform<ScalarType>::New();
}
mitk::AbstractTransformGeometry::AbstractTransformGeometry(const AbstractTransformGeometry &other)
: Superclass(other), m_ParametricBoundingBox(other.m_ParametricBoundingBox)
{
if (other.m_ParametricBoundingBox.IsNotNull())
{
m_ParametricBoundingBox = other.m_ParametricBoundingBox->DeepCopy();
this->SetParametricBounds(m_ParametricBoundingBox->GetBounds());
}
this->SetPlane(other.m_Plane);
this->SetFrameGeometry(other.m_FrameGeometry);
m_ItkVtkAbstractTransform = itk::VtkAbstractTransform<ScalarType>::New();
}
mitk::AbstractTransformGeometry::~AbstractTransformGeometry()
{
}
vtkAbstractTransform *mitk::AbstractTransformGeometry::GetVtkAbstractTransform() const
{
return m_ItkVtkAbstractTransform->GetVtkAbstractTransform();
}
mitk::ScalarType mitk::AbstractTransformGeometry::GetParametricExtentInMM(int direction) const
{
if (m_Plane.IsNull())
{
itkExceptionMacro(<< "m_Plane is nullptr.");
}
return m_Plane->GetExtentInMM(direction);
}
const itk::Transform<mitk::ScalarType, 3, 3> *mitk::AbstractTransformGeometry::GetParametricTransform() const
{
return m_ItkVtkAbstractTransform;
}
bool mitk::AbstractTransformGeometry::Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const
{
assert(this->IsBoundingBoxNull() == false);
mitk::Point2D pt2d_mm;
bool isInside;
isInside = Map(pt3d_mm, pt2d_mm);
Map(pt2d_mm, projectedPt3d_mm);
return isInside;
// Point3D pt3d_units;
// pt3d_units = m_ItkVtkAbstractTransform->BackTransform(pt3d_mm);
// pt3d_units[2] = 0;
// projectedPt3d_mm = m_ItkVtkAbstractTransform->TransformPoint(pt3d_units);
// return const_cast<BoundingBox*>(m_BoundingBox.GetPointer())->IsInside(pt3d_units);
}
bool mitk::AbstractTransformGeometry::Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const
{
assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull()));
Point3D pt3d_units;
pt3d_units = m_ItkVtkAbstractTransform->BackTransform(pt3d_mm);
return m_Plane->Map(pt3d_units, pt2d_mm);
}
void mitk::AbstractTransformGeometry::Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const
{
assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull()));
m_Plane->Map(pt2d_mm, pt3d_mm);
pt3d_mm = m_ItkVtkAbstractTransform->TransformPoint(pt3d_mm);
}
bool mitk::AbstractTransformGeometry::Project(const mitk::Point3D &atPt3d_mm,
const mitk::Vector3D &vec3d_mm,
mitk::Vector3D &projectedVec3d_mm) const
{
itkExceptionMacro("not implemented yet - replace GetIndexToWorldTransform by "
"m_ItkVtkAbstractTransform->GetInverseVtkAbstractTransform()");
assert(this->IsBoundingBoxNull() == false);
- Vector3D vec3d_units;
- vec3d_units = GetIndexToWorldTransform()->GetInverseMatrix() * vec3d_mm;
+ auto inverse = mitk::AffineTransform3D::New();
+ GetIndexToWorldTransform()->GetInverse(inverse);
+
+ Vector3D vec3d_units = inverse->GetMatrix() * vec3d_mm;
vec3d_units[2] = 0;
projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units);
Point3D pt3d_units;
mitk::ScalarType temp[3];
unsigned int i, j;
for (j = 0; j < 3; ++j)
temp[j] = atPt3d_mm[j] - GetIndexToWorldTransform()->GetOffset()[j];
for (i = 0; i < 3; ++i)
{
pt3d_units[i] = 0.0;
for (j = 0; j < 3; ++j)
- pt3d_units[i] += GetIndexToWorldTransform()->GetInverseMatrix()[i][j] * temp[j];
+ pt3d_units[i] += inverse->GetMatrix()[i][j] * temp[j];
}
return this->GetBoundingBox()->IsInside(pt3d_units);
}
bool mitk::AbstractTransformGeometry::Project(const mitk::Vector3D & /*vec3d_mm*/,
mitk::Vector3D & /*projectedVec3d_mm*/) const
{
MITK_WARN << "Need additional point! No standard value defined. Please use Project(const mitk::Point3D & atPt3d_mm, "
"const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm). Unfortunatley this one is not "
"implemented at the moment. Sorry :(";
itkExceptionMacro("not implemented yet - replace GetIndexToWorldTransform by "
"m_ItkVtkAbstractTransform->GetInverseVtkAbstractTransform()");
return false;
}
bool mitk::AbstractTransformGeometry::Map(const mitk::Point3D &atPt3d_mm,
const mitk::Vector3D &vec3d_mm,
mitk::Vector2D &vec2d_mm) const
{
assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull()));
ScalarType vtkpt[3], vtkvec[3];
itk2vtk(atPt3d_mm, vtkpt);
itk2vtk(vec3d_mm, vtkvec);
m_ItkVtkAbstractTransform->GetInverseVtkAbstractTransform()->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec);
mitk::Vector3D vec3d_units;
vtk2itk(vtkvec, vec3d_units);
return m_Plane->Map(atPt3d_mm, vec3d_units, vec2d_mm);
}
void mitk::AbstractTransformGeometry::Map(const mitk::Point2D &atPt2d_mm,
const mitk::Vector2D &vec2d_mm,
mitk::Vector3D &vec3d_mm) const
{
m_Plane->Map(atPt2d_mm, vec2d_mm, vec3d_mm);
Point3D atPt3d_mm;
Map(atPt2d_mm, atPt3d_mm);
float vtkpt[3], vtkvec[3];
itk2vtk(atPt3d_mm, vtkpt);
itk2vtk(vec3d_mm, vtkvec);
m_ItkVtkAbstractTransform->GetVtkAbstractTransform()->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec);
vtk2itk(vtkvec, vec3d_mm);
}
void mitk::AbstractTransformGeometry::IndexToWorld(const mitk::Point2D &pt_units, mitk::Point2D &pt_mm) const
{
m_Plane->IndexToWorld(pt_units, pt_mm);
}
void mitk::AbstractTransformGeometry::WorldToIndex(const mitk::Point2D &pt_mm, mitk::Point2D &pt_units) const
{
m_Plane->WorldToIndex(pt_mm, pt_units);
}
void mitk::AbstractTransformGeometry::IndexToWorld(const mitk::Point2D & /*atPt2d_units*/,
const mitk::Vector2D &vec_units,
mitk::Vector2D &vec_mm) const
{
MITK_WARN << "Warning! Call of the deprecated function AbstractTransformGeometry::IndexToWorld(point, vec, vec). Use "
"AbstractTransformGeometry::IndexToWorld(vec, vec) instead!";
this->IndexToWorld(vec_units, vec_mm);
}
void mitk::AbstractTransformGeometry::IndexToWorld(const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const
{
m_Plane->IndexToWorld(vec_units, vec_mm);
}
void mitk::AbstractTransformGeometry::WorldToIndex(const mitk::Point2D & /*atPt2d_mm*/,
const mitk::Vector2D &vec_mm,
mitk::Vector2D &vec_units) const
{
MITK_WARN << "Warning! Call of the deprecated function AbstractTransformGeometry::WorldToIndex(point, vec, vec). Use "
"AbstractTransformGeometry::WorldToIndex(vec, vec) instead!";
this->WorldToIndex(vec_mm, vec_units);
}
void mitk::AbstractTransformGeometry::WorldToIndex(const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const
{
m_Plane->WorldToIndex(vec_mm, vec_units);
}
bool mitk::AbstractTransformGeometry::IsAbove(const mitk::Point3D &pt3d_mm, bool /*considerBoundingBox*/) const
{
assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull()));
Point3D pt3d_ParametricWorld;
pt3d_ParametricWorld = m_ItkVtkAbstractTransform->BackTransform(pt3d_mm);
Point3D pt3d_ParametricUnits;
((BaseGeometry *)m_Plane)->WorldToIndex(pt3d_ParametricWorld, pt3d_ParametricUnits);
return (pt3d_ParametricUnits[2] > m_ParametricBoundingBox->GetBounds()[4]);
}
void mitk::AbstractTransformGeometry::SetVtkAbstractTransform(vtkAbstractTransform *aVtkAbstractTransform)
{
m_ItkVtkAbstractTransform->SetVtkAbstractTransform(aVtkAbstractTransform);
}
void mitk::AbstractTransformGeometry::SetPlane(const mitk::PlaneGeometry *aPlane)
{
if (aPlane != nullptr)
{
m_Plane = static_cast<mitk::PlaneGeometry *>(aPlane->Clone().GetPointer());
BoundingBox::BoundsArrayType b = m_Plane->GetBoundingBox()->GetBounds();
SetParametricBounds(b);
CalculateFrameGeometry();
}
else
{
if (m_Plane.IsNull())
return;
m_Plane = nullptr;
}
Modified();
}
void mitk::AbstractTransformGeometry::CalculateFrameGeometry()
{
if ((m_Plane.IsNull()) || (m_FrameGeometry.IsNotNull()))
return;
//@warning affine-transforms and bounding-box should be set by specific sub-classes!
SetBounds(m_Plane->GetBoundingBox()->GetBounds());
}
void mitk::AbstractTransformGeometry::SetFrameGeometry(const mitk::BaseGeometry *frameGeometry)
{
if ((frameGeometry != nullptr) && (frameGeometry->IsValid()))
{
m_FrameGeometry = static_cast<mitk::BaseGeometry *>(frameGeometry->Clone().GetPointer());
SetIndexToWorldTransform(m_FrameGeometry->GetIndexToWorldTransform());
SetBounds(m_FrameGeometry->GetBounds());
}
else
{
m_FrameGeometry = nullptr;
}
}
-unsigned long mitk::AbstractTransformGeometry::GetMTime() const
+itk::ModifiedTimeType mitk::AbstractTransformGeometry::GetMTime() const
{
if (Superclass::GetMTime() < m_ItkVtkAbstractTransform->GetMTime())
return m_ItkVtkAbstractTransform->GetMTime();
return Superclass::GetMTime();
}
void mitk::AbstractTransformGeometry::SetOversampling(mitk::ScalarType oversampling)
{
if (m_Plane.IsNull())
{
itkExceptionMacro(<< "m_Plane is not set.");
}
mitk::BoundingBox::BoundsArrayType bounds = m_Plane->GetBounds();
bounds[1] *= oversampling;
bounds[3] *= oversampling;
bounds[5] *= oversampling;
SetParametricBounds(bounds);
}
itk::LightObject::Pointer mitk::AbstractTransformGeometry::InternalClone() const
{
Self::Pointer newGeometry = new AbstractTransformGeometry(*this);
newGeometry->UnRegister();
return newGeometry.GetPointer();
}
void mitk::AbstractTransformGeometry::SetParametricBounds(const BoundingBox::BoundsArrayType &bounds)
{
m_ParametricBoundingBox = BoundingBoxType::New();
BoundingBoxType::PointsContainer::Pointer pointscontainer = BoundingBoxType::PointsContainer::New();
BoundingBoxType::PointType p;
BoundingBoxType::PointIdentifier pointid;
for (pointid = 0; pointid < 2; ++pointid)
{
unsigned int i;
for (i = 0; i < GetNDimensions(); ++i)
{
p[i] = bounds[2 * i + pointid];
}
pointscontainer->InsertElement(pointid, p);
}
m_ParametricBoundingBox->SetPoints(pointscontainer);
m_ParametricBoundingBox->ComputeBoundingBox();
this->Modified();
}
const mitk::BoundingBox::BoundsArrayType &mitk::AbstractTransformGeometry::GetParametricBounds() const
{
assert(m_ParametricBoundingBox.IsNotNull());
return m_ParametricBoundingBox->GetBounds();
}
mitk::ScalarType mitk::AbstractTransformGeometry::GetParametricExtent(int direction) const
{
if (direction < 0 || direction >= 3)
mitkThrow() << "Invalid direction. Must be between either 0, 1 or 2. ";
assert(m_ParametricBoundingBox.IsNotNull());
BoundingBoxType::BoundsArrayType bounds = m_ParametricBoundingBox->GetBounds();
return bounds[direction * 2 + 1] - bounds[direction * 2];
}
diff --git a/Modules/Core/src/DataManagement/mitkBaseData.cpp b/Modules/Core/src/DataManagement/mitkBaseData.cpp
index 9f3b670884..3750bd4848 100644
--- a/Modules/Core/src/DataManagement/mitkBaseData.cpp
+++ b/Modules/Core/src/DataManagement/mitkBaseData.cpp
@@ -1,348 +1,348 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkBaseData.h"
#include <itkObjectFactoryBase.h>
#include <mitkException.h>
#include <mitkGeometry3D.h>
#include <mitkProportionalTimeGeometry.h>
#include <mitkStringProperty.h>
mitk::BaseData::BaseData()
: m_SourceOutputIndexDuplicate(0),
m_Initialized(true),
m_PropertyList(PropertyList::New()),
m_TimeGeometry(ProportionalTimeGeometry::New())
{
}
mitk::BaseData::BaseData(const BaseData &other)
: itk::DataObject(),
OperationActor(),
Identifiable(),
m_SourceOutputIndexDuplicate(other.m_SourceOutputIndexDuplicate),
m_Initialized(other.m_Initialized),
m_PropertyList(other.m_PropertyList->Clone()),
m_TimeGeometry(other.m_TimeGeometry->Clone())
{
}
mitk::BaseData::~BaseData()
{
}
void mitk::BaseData::InitializeTimeGeometry(unsigned int timeSteps)
{
mitk::Geometry3D::Pointer geo3D = mitk::Geometry3D::New();
mitk::BaseGeometry::Pointer baseGeo = dynamic_cast<BaseGeometry *>(geo3D.GetPointer());
baseGeo->Initialize();
// The geometry is propagated automatically to the other items,
// if EvenlyTimed is true...
// Old timeGeometry->InitializeEvenlyTimed( g3d.GetPointer(), timeSteps );
TimeGeometry::Pointer timeGeometry = this->GetTimeGeometry();
timeGeometry->Initialize();
timeGeometry->Expand(timeSteps);
for (TimeStepType step = 0; step < timeSteps; ++step)
{
timeGeometry->SetTimeStepGeometry(baseGeo.GetPointer(), step);
}
}
void mitk::BaseData::UpdateOutputInformation()
{
if (this->GetSource())
{
this->GetSource()->UpdateOutputInformation();
}
if (m_TimeGeometry.IsNotNull())
{
m_TimeGeometry->UpdateBoundingBox();
}
}
const mitk::TimeGeometry *mitk::BaseData::GetUpdatedTimeGeometry()
{
SetRequestedRegionToLargestPossibleRegion();
UpdateOutputInformation();
return GetTimeGeometry();
}
void mitk::BaseData::Expand(unsigned int timeSteps)
{
if (m_TimeGeometry.IsNotNull())
{
m_TimeGeometry->Expand(timeSteps);
}
else
{
this->InitializeTimeGeometry(timeSteps);
}
}
const mitk::BaseGeometry *mitk::BaseData::GetUpdatedGeometry(int t)
{
SetRequestedRegionToLargestPossibleRegion();
UpdateOutputInformation();
return GetGeometry(t);
}
void mitk::BaseData::SetGeometry(BaseGeometry *geometry)
{
ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New();
if (geometry != nullptr)
{
timeGeometry->Initialize(geometry, 1);
}
SetTimeGeometry(timeGeometry);
return;
}
void mitk::BaseData::SetTimeGeometry(TimeGeometry *geometry)
{
m_TimeGeometry = geometry;
this->Modified();
}
void mitk::BaseData::SetClonedGeometry(const BaseGeometry *aGeometry3D)
{
SetGeometry(static_cast<mitk::BaseGeometry *>(aGeometry3D->Clone().GetPointer()));
}
void mitk::BaseData::SetClonedTimeGeometry(const TimeGeometry *geometry)
{
TimeGeometry::Pointer clonedGeometry = geometry->Clone();
SetTimeGeometry(clonedGeometry.GetPointer());
}
void mitk::BaseData::SetClonedGeometry(const BaseGeometry *aGeometry3D, unsigned int time)
{
if (m_TimeGeometry)
{
m_TimeGeometry->SetTimeStepGeometry(static_cast<mitk::BaseGeometry *>(aGeometry3D->Clone().GetPointer()), time);
}
}
bool mitk::BaseData::IsEmptyTimeStep(unsigned int) const
{
return IsInitialized() == false;
}
bool mitk::BaseData::IsEmpty() const
{
if (IsInitialized() == false)
return true;
const TimeGeometry *timeGeometry = const_cast<BaseData *>(this)->GetUpdatedTimeGeometry();
if (timeGeometry == nullptr)
return true;
unsigned int timeSteps = timeGeometry->CountTimeSteps();
for (unsigned int t = 0; t < timeSteps; ++t)
{
if (IsEmptyTimeStep(t) == false)
return false;
}
return true;
}
itk::SmartPointer<mitk::BaseDataSource> mitk::BaseData::GetSource() const
{
return static_cast<mitk::BaseDataSource *>(Superclass::GetSource().GetPointer());
}
mitk::PropertyList::Pointer mitk::BaseData::GetPropertyList() const
{
return m_PropertyList;
}
mitk::BaseProperty::Pointer mitk::BaseData::GetProperty(const char *propertyKey) const
{
return m_PropertyList->GetProperty(propertyKey);
}
void mitk::BaseData::SetProperty(const char *propertyKey, BaseProperty *propertyValue)
{
m_PropertyList->SetProperty(propertyKey, propertyValue);
}
void mitk::BaseData::SetPropertyList(PropertyList *pList)
{
m_PropertyList = pList;
}
void mitk::BaseData::SetOrigin(const mitk::Point3D &origin)
{
TimeGeometry *timeGeom = GetTimeGeometry();
assert(timeGeom != nullptr);
TimeStepType steps = timeGeom->CountTimeSteps();
for (TimeStepType timestep = 0; timestep < steps; ++timestep)
{
auto geometry = GetGeometry(timestep);
if (geometry != nullptr)
{
geometry->SetOrigin(origin);
}
}
}
-unsigned long mitk::BaseData::GetMTime() const
+itk::ModifiedTimeType mitk::BaseData::GetMTime() const
{
- unsigned long time = Superclass::GetMTime();
+ auto time = Superclass::GetMTime();
if (m_TimeGeometry.IsNotNull())
{
if ((time < m_TimeGeometry->GetMTime()))
{
return m_TimeGeometry->GetMTime();
}
}
return time;
}
void mitk::BaseData::Graft(const itk::DataObject *)
{
itkExceptionMacro(<< "Graft not implemented for mitk::BaseData subclass " << this->GetNameOfClass())
}
void mitk::BaseData::CopyInformation(const itk::DataObject *data)
{
const auto *bd = dynamic_cast<const Self *>(data);
if (bd != nullptr)
{
m_PropertyList = bd->GetPropertyList()->Clone();
if (bd->GetTimeGeometry() != nullptr)
{
m_TimeGeometry = bd->GetTimeGeometry()->Clone();
}
}
else
{
// pointer could not be cast back down; this can be the case if your filters input
// and output objects differ in type; then you have to write your own GenerateOutputInformation method
itkExceptionMacro(<< "mitk::BaseData::CopyInformation() cannot cast " << typeid(data).name() << " to "
<< typeid(Self *).name());
}
}
bool mitk::BaseData::IsInitialized() const
{
return m_Initialized;
}
void mitk::BaseData::Clear()
{
this->ClearData();
this->InitializeEmpty();
}
void mitk::BaseData::ClearData()
{
if (m_Initialized)
{
ReleaseData();
m_Initialized = false;
}
}
void mitk::BaseData::ExecuteOperation(mitk::Operation * /*operation*/)
{
// empty by default. override if needed!
}
void mitk::BaseData::PrintSelf(std::ostream &os, itk::Indent indent) const
{
os << std::endl;
os << indent << " TimeGeometry: ";
if (GetTimeGeometry() == nullptr)
os << "nullptr" << std::endl;
else
GetTimeGeometry()->Print(os, indent);
// print out all properties
PropertyList::Pointer propertyList = this->GetPropertyList();
if (propertyList.IsNotNull() && !propertyList->IsEmpty())
{
// general headline
os << "Properties of BaseData:" << std::endl;
const PropertyList::PropertyMap *map = propertyList->GetMap();
for (auto iter = map->begin(); iter != map->end(); ++iter)
{
os << " " << (*iter).first << " " << (*iter).second->GetValueAsString() << std::endl;
}
}
}
mitk::BaseProperty::ConstPointer mitk::BaseData::GetConstProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext) const
{
if (propertyKey.empty())
return nullptr;
if (contextName.empty() || fallBackOnDefaultContext)
return m_PropertyList->GetProperty(propertyKey);
return nullptr;
}
mitk::BaseProperty * mitk::BaseData::GetNonConstProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext)
{
if (propertyKey.empty())
return nullptr;
if (contextName.empty() || fallBackOnDefaultContext)
return m_PropertyList->GetProperty(propertyKey);
return nullptr;
}
void mitk::BaseData::SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName, bool fallBackOnDefaultContext)
{
if (propertyKey.empty())
mitkThrow() << "Property key is empty.";
if (contextName.empty() || fallBackOnDefaultContext)
{
m_PropertyList->SetProperty(propertyKey, property);
return;
}
mitkThrow() << "Unknown or unsupported non-default property context.";
}
void mitk::BaseData::RemoveProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext)
{
if (propertyKey.empty())
mitkThrow() << "Property key is empty.";
if (contextName.empty() || fallBackOnDefaultContext)
{
m_PropertyList->RemoveProperty(propertyKey);
return;
}
mitkThrow() << "Unknown or unsupported non-default property context.";
}
std::vector<std::string> mitk::BaseData::GetPropertyKeys(const std::string &/*contextName*/, bool /*includeDefaultContext*/) const
{
return m_PropertyList->GetPropertyKeys();
}
std::vector<std::string> mitk::BaseData::GetPropertyContextNames() const
{
return std::vector<std::string>();
}
diff --git a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp
index f1323caf3f..09493418f5 100644
--- a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp
+++ b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp
@@ -1,1097 +1,1101 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <iomanip>
#include <sstream>
#include <bitset>
#include <vtkMatrix4x4.h>
#include <vtkMatrixToLinearTransform.h>
#include "mitkApplyTransformMatrixOperation.h"
#include "mitkBaseGeometry.h"
#include "mitkGeometryTransformHolder.h"
#include "mitkInteractionConst.h"
#include "mitkMatrixConvert.h"
#include "mitkModifiedLock.h"
#include "mitkPointOperation.h"
#include "mitkRestorePlanePositionOperation.h"
#include "mitkRotationOperation.h"
#include "mitkScaleOperation.h"
#include "mitkVector.h"
#include "mitkMatrix.h"
mitk::BaseGeometry::BaseGeometry()
: Superclass(),
mitk::OperationActor(),
m_FrameOfReferenceID(0),
m_IndexToWorldTransformLastModified(0),
m_ImageGeometry(false),
m_ModifiedLockFlag(false),
m_ModifiedCalledFlag(false)
{
m_GeometryTransform = new GeometryTransformHolder();
Initialize();
}
mitk::BaseGeometry::BaseGeometry(const BaseGeometry &other)
: Superclass(),
mitk::OperationActor(),
m_FrameOfReferenceID(other.m_FrameOfReferenceID),
m_IndexToWorldTransformLastModified(other.m_IndexToWorldTransformLastModified),
m_ImageGeometry(other.m_ImageGeometry),
m_ModifiedLockFlag(false),
m_ModifiedCalledFlag(false)
{
m_GeometryTransform = new GeometryTransformHolder(*other.GetGeometryTransformHolder());
other.InitializeGeometry(this);
}
mitk::BaseGeometry::~BaseGeometry()
{
delete m_GeometryTransform;
}
void mitk::BaseGeometry::SetVtkMatrixDeepCopy(vtkTransform *vtktransform)
{
m_GeometryTransform->SetVtkMatrixDeepCopy(vtktransform);
}
const mitk::Point3D mitk::BaseGeometry::GetOrigin() const
{
return m_GeometryTransform->GetOrigin();
}
void mitk::BaseGeometry::SetOrigin(const Point3D &origin)
{
mitk::ModifiedLock lock(this);
if (origin != GetOrigin())
{
m_GeometryTransform->SetOrigin(origin);
Modified();
}
}
const mitk::Vector3D mitk::BaseGeometry::GetSpacing() const
{
return m_GeometryTransform->GetSpacing();
}
void mitk::BaseGeometry::Initialize()
{
float b[6] = {0, 1, 0, 1, 0, 1};
SetFloatBounds(b);
m_GeometryTransform->Initialize();
m_FrameOfReferenceID = 0;
m_ImageGeometry = false;
}
void mitk::BaseGeometry::SetFloatBounds(const float bounds[6])
{
mitk::BoundingBox::BoundsArrayType b;
const float *input = bounds;
int i = 0;
for (mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6; ++i)
*it++ = (mitk::ScalarType)*input++;
SetBounds(b);
}
void mitk::BaseGeometry::SetFloatBounds(const double bounds[6])
{
mitk::BoundingBox::BoundsArrayType b;
const double *input = bounds;
int i = 0;
for (mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6; ++i)
*it++ = (mitk::ScalarType)*input++;
SetBounds(b);
}
/** Initialize the geometry */
void mitk::BaseGeometry::InitializeGeometry(BaseGeometry *newGeometry) const
{
newGeometry->SetBounds(m_BoundingBox->GetBounds());
newGeometry->SetFrameOfReferenceID(GetFrameOfReferenceID());
newGeometry->InitializeGeometryTransformHolder(this);
newGeometry->m_ImageGeometry = m_ImageGeometry;
}
void mitk::BaseGeometry::InitializeGeometryTransformHolder(const BaseGeometry *otherGeometry)
{
this->m_GeometryTransform->Initialize(otherGeometry->GetGeometryTransformHolder());
}
/** Set the bounds */
void mitk::BaseGeometry::SetBounds(const BoundsArrayType &bounds)
{
mitk::ModifiedLock lock(this);
this->CheckBounds(bounds);
m_BoundingBox = BoundingBoxType::New();
BoundingBoxType::PointsContainer::Pointer pointscontainer = BoundingBoxType::PointsContainer::New();
BoundingBoxType::PointType p;
BoundingBoxType::PointIdentifier pointid;
for (pointid = 0; pointid < 2; ++pointid)
{
unsigned int i;
for (i = 0; i < m_NDimensions; ++i)
{
p[i] = bounds[2 * i + pointid];
}
pointscontainer->InsertElement(pointid, p);
}
m_BoundingBox->SetPoints(pointscontainer);
m_BoundingBox->ComputeBoundingBox();
this->Modified();
}
void mitk::BaseGeometry::SetIndexToWorldTransform(mitk::AffineTransform3D *transform)
{
mitk::ModifiedLock lock(this);
CheckIndexToWorldTransform(transform);
m_GeometryTransform->SetIndexToWorldTransform(transform);
Modified();
}
void mitk::BaseGeometry::SetIndexToWorldTransformWithoutChangingSpacing(mitk::AffineTransform3D *transform)
{
// security check
mitk::Vector3D originalSpacing = this->GetSpacing();
mitk::ModifiedLock lock(this);
CheckIndexToWorldTransform(transform);
m_GeometryTransform->SetIndexToWorldTransformWithoutChangingSpacing(transform);
Modified();
// Security check. Spacig must not have changed
if (!mitk::Equal(originalSpacing, this->GetSpacing()))
{
MITK_WARN << "Spacing has changed in a method, where the spacing must not change.";
assert(false);
}
}
const mitk::BaseGeometry::BoundsArrayType mitk::BaseGeometry::GetBounds() const
{
assert(m_BoundingBox.IsNotNull());
return m_BoundingBox->GetBounds();
}
bool mitk::BaseGeometry::IsValid() const
{
return true;
}
void mitk::BaseGeometry::SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing)
{
PreSetSpacing(aSpacing);
_SetSpacing(aSpacing, enforceSetSpacing);
}
void mitk::BaseGeometry::_SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing)
{
m_GeometryTransform->SetSpacing(aSpacing, enforceSetSpacing);
}
mitk::Vector3D mitk::BaseGeometry::GetAxisVector(unsigned int direction) const
{
Vector3D frontToBack;
- frontToBack.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction));
+ frontToBack.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction).as_ref());
frontToBack *= GetExtent(direction);
return frontToBack;
}
mitk::ScalarType mitk::BaseGeometry::GetExtent(unsigned int direction) const
{
assert(m_BoundingBox.IsNotNull());
if (direction >= m_NDimensions)
mitkThrow() << "Direction is too big. This geometry is for 3D Data";
BoundsArrayType bounds = m_BoundingBox->GetBounds();
return bounds[direction * 2 + 1] - bounds[direction * 2];
}
bool mitk::BaseGeometry::Is2DConvertable()
{
bool isConvertableWithoutLoss = true;
do
{
if (this->GetSpacing()[2] != 1)
{
isConvertableWithoutLoss = false;
break;
}
if (this->GetOrigin()[2] != 0)
{
isConvertableWithoutLoss = false;
break;
}
mitk::Vector3D col0, col1, col2;
- col0.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0));
- col1.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1));
- col2.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2));
+ col0.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).as_ref());
+ col1.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).as_ref());
+ col2.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref());
if ((col0[2] != 0) || (col1[2] != 0) || (col2[0] != 0) || (col2[1] != 0) || (col2[2] != 1))
{
isConvertableWithoutLoss = false;
break;
}
} while (false);
return isConvertableWithoutLoss;
}
mitk::Point3D mitk::BaseGeometry::GetCenter() const
{
assert(m_BoundingBox.IsNotNull());
Point3D c = m_BoundingBox->GetCenter();
if (m_ImageGeometry)
{
// Get Center returns the middel of min and max pixel index. In corner based images, this is the right position.
// In center based images (imageGeometry == true), the index needs to be shifted back.
c[0] -= 0.5;
c[1] -= 0.5;
c[2] -= 0.5;
}
this->IndexToWorld(c, c);
return c;
}
double mitk::BaseGeometry::GetDiagonalLength2() const
{
Vector3D diagonalvector = GetCornerPoint() - GetCornerPoint(false, false, false);
return diagonalvector.GetSquaredNorm();
}
double mitk::BaseGeometry::GetDiagonalLength() const
{
return sqrt(GetDiagonalLength2());
}
mitk::Point3D mitk::BaseGeometry::GetCornerPoint(int id) const
{
assert(id >= 0);
assert(this->IsBoundingBoxNull() == false);
BoundingBox::BoundsArrayType bounds = this->GetBoundingBox()->GetBounds();
Point3D cornerpoint;
switch (id)
{
case 0:
FillVector3D(cornerpoint, bounds[0], bounds[2], bounds[4]);
break;
case 1:
FillVector3D(cornerpoint, bounds[0], bounds[2], bounds[5]);
break;
case 2:
FillVector3D(cornerpoint, bounds[0], bounds[3], bounds[4]);
break;
case 3:
FillVector3D(cornerpoint, bounds[0], bounds[3], bounds[5]);
break;
case 4:
FillVector3D(cornerpoint, bounds[1], bounds[2], bounds[4]);
break;
case 5:
FillVector3D(cornerpoint, bounds[1], bounds[2], bounds[5]);
break;
case 6:
FillVector3D(cornerpoint, bounds[1], bounds[3], bounds[4]);
break;
case 7:
FillVector3D(cornerpoint, bounds[1], bounds[3], bounds[5]);
break;
default:
{
itkExceptionMacro(<< "A cube only has 8 corners. These are labeled 0-7.");
}
}
if (m_ImageGeometry)
{
// Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the
// bounding box. The bounding box itself is no image, so it is corner-based
FillVector3D(cornerpoint, cornerpoint[0] - 0.5, cornerpoint[1] - 0.5, cornerpoint[2] - 0.5);
}
return this->GetIndexToWorldTransform()->TransformPoint(cornerpoint);
}
mitk::Point3D mitk::BaseGeometry::GetCornerPoint(bool xFront, bool yFront, bool zFront) const
{
assert(this->IsBoundingBoxNull() == false);
BoundingBox::BoundsArrayType bounds = this->GetBoundingBox()->GetBounds();
Point3D cornerpoint;
cornerpoint[0] = (xFront ? bounds[0] : bounds[1]);
cornerpoint[1] = (yFront ? bounds[2] : bounds[3]);
cornerpoint[2] = (zFront ? bounds[4] : bounds[5]);
if (m_ImageGeometry)
{
// Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the
// bounding box. The bounding box itself is no image, so it is corner-based
FillVector3D(cornerpoint, cornerpoint[0] - 0.5, cornerpoint[1] - 0.5, cornerpoint[2] - 0.5);
}
return this->GetIndexToWorldTransform()->TransformPoint(cornerpoint);
}
mitk::ScalarType mitk::BaseGeometry::GetExtentInMM(int direction) const
{
return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction).magnitude() *
GetExtent(direction);
}
void mitk::BaseGeometry::SetExtentInMM(int direction, ScalarType extentInMM)
{
mitk::ModifiedLock lock(this);
ScalarType len = GetExtentInMM(direction);
if (fabs(len - extentInMM) >= mitk::eps)
{
AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix;
vnlmatrix = m_GeometryTransform->GetVnlMatrix();
if (len > extentInMM)
vnlmatrix.set_column(direction, vnlmatrix.get_column(direction) / len * extentInMM);
else
vnlmatrix.set_column(direction, vnlmatrix.get_column(direction) * extentInMM / len);
Matrix3D matrix;
matrix = vnlmatrix;
m_GeometryTransform->SetMatrix(matrix);
Modified();
}
}
bool mitk::BaseGeometry::IsInside(const mitk::Point3D &p) const
{
mitk::Point3D index;
WorldToIndex(p, index);
return IsIndexInside(index);
}
bool mitk::BaseGeometry::IsIndexInside(const mitk::Point3D &index) const
{
bool inside = false;
// if it is an image geometry, we need to convert the index to discrete values
// this is done by applying the rounding function also used in WorldToIndex (see line 323)
if (m_ImageGeometry)
{
mitk::Point3D discretIndex;
discretIndex[0] = itk::Math::RoundHalfIntegerUp<mitk::ScalarType>(index[0]);
discretIndex[1] = itk::Math::RoundHalfIntegerUp<mitk::ScalarType>(index[1]);
discretIndex[2] = itk::Math::RoundHalfIntegerUp<mitk::ScalarType>(index[2]);
inside = this->GetBoundingBox()->IsInside(discretIndex);
// we have to check if the index is at the upper border of each dimension,
// because the boundingbox is not centerbased
if (inside)
{
const BoundingBox::BoundsArrayType &bounds = this->GetBoundingBox()->GetBounds();
if ((discretIndex[0] == bounds[1]) || (discretIndex[1] == bounds[3]) || (discretIndex[2] == bounds[5]))
inside = false;
}
}
else
inside = this->GetBoundingBox()->IsInside(index);
return inside;
}
void mitk::BaseGeometry::WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const
{
mitk::Vector3D tempIn, tempOut;
const TransformType::OffsetType &offset = this->GetIndexToWorldTransform()->GetOffset();
tempIn = pt_mm.GetVectorFromOrigin() - offset;
WorldToIndex(tempIn, tempOut);
pt_units = Point3D(tempOut);
}
void mitk::BaseGeometry::WorldToIndex(const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const
{
// Get WorldToIndex transform
if (m_IndexToWorldTransformLastModified != this->GetIndexToWorldTransform()->GetMTime())
{
if (!m_InvertedTransform)
{
m_InvertedTransform = TransformType::New();
}
if (!this->GetIndexToWorldTransform()->GetInverse(m_InvertedTransform.GetPointer()))
{
itkExceptionMacro("Internal ITK matrix inversion error, cannot proceed.");
}
m_IndexToWorldTransformLastModified = this->GetIndexToWorldTransform()->GetMTime();
}
// Check for valid matrix inversion
const TransformType::MatrixType &inverse = m_InvertedTransform->GetMatrix();
if (inverse.GetVnlMatrix().has_nans())
{
itkExceptionMacro("Internal ITK matrix inversion error, cannot proceed. Matrix was: "
<< std::endl
<< this->GetIndexToWorldTransform()->GetMatrix()
<< "Suggested inverted matrix is:"
<< std::endl
<< inverse);
}
vec_units = inverse * vec_mm;
}
void mitk::BaseGeometry::WorldToIndex(const mitk::Point3D & /*atPt3d_mm*/,
const mitk::Vector3D &vec_mm,
mitk::Vector3D &vec_units) const
{
MITK_WARN << "Warning! Call of the deprecated function BaseGeometry::WorldToIndex(point, vec, vec). Use "
"BaseGeometry::WorldToIndex(vec, vec) instead!";
this->WorldToIndex(vec_mm, vec_units);
}
mitk::VnlVector mitk::BaseGeometry::GetOriginVnl() const
{
return GetOrigin().GetVnlVector();
}
vtkLinearTransform *mitk::BaseGeometry::GetVtkTransform() const
{
return m_GeometryTransform->GetVtkTransform();
}
void mitk::BaseGeometry::SetIdentity()
{
mitk::ModifiedLock lock(this);
m_GeometryTransform->SetIdentity();
Modified();
}
void mitk::BaseGeometry::Compose(const mitk::BaseGeometry::TransformType *other, bool pre)
{
mitk::ModifiedLock lock(this);
m_GeometryTransform->Compose(other, pre);
Modified();
}
void mitk::BaseGeometry::Compose(const vtkMatrix4x4 *vtkmatrix, bool pre)
{
mitk::BaseGeometry::TransformType::Pointer itkTransform = mitk::BaseGeometry::TransformType::New();
TransferVtkMatrixToItkTransform(vtkmatrix, itkTransform.GetPointer());
Compose(itkTransform, pre);
}
void mitk::BaseGeometry::Translate(const Vector3D &vector)
{
if ((vector[0] != 0) || (vector[1] != 0) || (vector[2] != 0))
{
this->SetOrigin(this->GetOrigin() + vector);
}
}
void mitk::BaseGeometry::IndexToWorld(const mitk::Point3D &pt_units, mitk::Point3D &pt_mm) const
{
pt_mm = this->GetIndexToWorldTransform()->TransformPoint(pt_units);
}
void mitk::BaseGeometry::IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const
{
vec_mm = this->GetIndexToWorldTransform()->TransformVector(vec_units);
}
void mitk::BaseGeometry::ExecuteOperation(Operation *operation)
{
mitk::ModifiedLock lock(this);
vtkTransform *vtktransform = vtkTransform::New();
vtktransform->SetMatrix(this->GetVtkMatrix());
switch (operation->GetOperationType())
{
case OpNOTHING:
break;
case OpMOVE:
{
auto *pointOp = dynamic_cast<mitk::PointOperation *>(operation);
if (pointOp == nullptr)
{
MITK_ERROR << "Point move operation is null!";
return;
}
mitk::Point3D newPos = pointOp->GetPoint();
ScalarType data[3];
vtktransform->GetPosition(data);
vtktransform->PostMultiply();
vtktransform->Translate(newPos[0], newPos[1], newPos[2]);
vtktransform->PreMultiply();
break;
}
case OpSCALE:
{
auto *scaleOp = dynamic_cast<mitk::ScaleOperation *>(operation);
if (scaleOp == nullptr)
{
MITK_ERROR << "Scale operation is null!";
return;
}
mitk::Point3D newScale = scaleOp->GetScaleFactor();
ScalarType scalefactor[3];
scalefactor[0] = 1 + (newScale[0] / GetMatrixColumn(0).magnitude());
scalefactor[1] = 1 + (newScale[1] / GetMatrixColumn(1).magnitude());
scalefactor[2] = 1 + (newScale[2] / GetMatrixColumn(2).magnitude());
mitk::Point3D anchor = scaleOp->GetScaleAnchorPoint();
vtktransform->PostMultiply();
vtktransform->Translate(-anchor[0], -anchor[1], -anchor[2]);
vtktransform->Scale(scalefactor[0], scalefactor[1], scalefactor[2]);
vtktransform->Translate(anchor[0], anchor[1], anchor[2]);
break;
}
case OpROTATE:
{
auto *rotateOp = dynamic_cast<mitk::RotationOperation *>(operation);
if (rotateOp == nullptr)
{
MITK_ERROR << "Rotation operation is null!";
return;
}
Vector3D rotationVector = rotateOp->GetVectorOfRotation();
Point3D center = rotateOp->GetCenterOfRotation();
ScalarType angle = rotateOp->GetAngleOfRotation();
vtktransform->PostMultiply();
vtktransform->Translate(-center[0], -center[1], -center[2]);
vtktransform->RotateWXYZ(angle, rotationVector[0], rotationVector[1], rotationVector[2]);
vtktransform->Translate(center[0], center[1], center[2]);
vtktransform->PreMultiply();
break;
}
case OpRESTOREPLANEPOSITION:
{
// Copy necessary to avoid vtk warning
vtkMatrix4x4 *matrix = vtkMatrix4x4::New();
TransferItkTransformToVtkMatrix(
dynamic_cast<mitk::RestorePlanePositionOperation *>(operation)->GetTransform().GetPointer(), matrix);
vtktransform->SetMatrix(matrix);
matrix->Delete();
break;
}
case OpAPPLYTRANSFORMMATRIX:
{
auto *applyMatrixOp = dynamic_cast<ApplyTransformMatrixOperation *>(operation);
vtktransform->SetMatrix(applyMatrixOp->GetMatrix());
break;
}
default:
vtktransform->Delete();
return;
}
this->SetVtkMatrixDeepCopy(vtktransform);
Modified();
vtktransform->Delete();
}
mitk::VnlVector mitk::BaseGeometry::GetMatrixColumn(unsigned int direction) const
{
- return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction);
+ return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction).as_ref();
}
mitk::BoundingBox::Pointer mitk::BaseGeometry::CalculateBoundingBoxRelativeToTransform(
const mitk::AffineTransform3D *transform) const
{
mitk::BoundingBox::PointsContainer::Pointer pointscontainer = mitk::BoundingBox::PointsContainer::New();
mitk::BoundingBox::PointIdentifier pointid = 0;
unsigned char i;
if (transform != nullptr)
{
mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New();
transform->GetInverse(inverse);
for (i = 0; i < 8; ++i)
pointscontainer->InsertElement(pointid++, inverse->TransformPoint(GetCornerPoint(i)));
}
else
{
for (i = 0; i < 8; ++i)
pointscontainer->InsertElement(pointid++, GetCornerPoint(i));
}
mitk::BoundingBox::Pointer result = mitk::BoundingBox::New();
result->SetPoints(pointscontainer);
result->ComputeBoundingBox();
return result;
}
const std::string mitk::BaseGeometry::GetTransformAsString(TransformType *transformType)
{
std::ostringstream out;
out << '[';
for (int i = 0; i < 3; ++i)
{
out << '[';
for (int j = 0; j < 3; ++j)
out << transformType->GetMatrix().GetVnlMatrix().get(i, j) << ' ';
out << ']';
}
out << "][";
for (int i = 0; i < 3; ++i)
out << transformType->GetOffset()[i] << ' ';
out << "]\0";
return out.str();
}
void mitk::BaseGeometry::SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4 *vtkmatrix)
{
m_GeometryTransform->SetIndexToWorldTransformByVtkMatrix(vtkmatrix);
}
void mitk::BaseGeometry::SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkMatrix4x4 *vtkmatrix)
{
m_GeometryTransform->SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkmatrix);
}
void mitk::BaseGeometry::IndexToWorld(const mitk::Point3D & /*atPt3d_units*/,
const mitk::Vector3D &vec_units,
mitk::Vector3D &vec_mm) const
{
MITK_WARN << "Warning! Call of the deprecated function BaseGeometry::IndexToWorld(point, vec, vec). Use "
"BaseGeometry::IndexToWorld(vec, vec) instead!";
// vec_mm = m_IndexToWorldTransform->TransformVector(vec_units);
this->IndexToWorld(vec_units, vec_mm);
}
vtkMatrix4x4 *mitk::BaseGeometry::GetVtkMatrix()
{
return m_GeometryTransform->GetVtkMatrix();
}
bool mitk::BaseGeometry::IsBoundingBoxNull() const
{
return m_BoundingBox.IsNull();
}
bool mitk::BaseGeometry::IsIndexToWorldTransformNull() const
{
return m_GeometryTransform->IsIndexToWorldTransformNull();
}
void mitk::BaseGeometry::ChangeImageGeometryConsideringOriginOffset(const bool isAnImageGeometry)
{
// If Geometry is switched to ImageGeometry, you have to put an offset to the origin, because
// imageGeometries origins are pixel-center-based
// ... and remove the offset, if you switch an imageGeometry back to a normal geometry
// For more information please see the Geometry documentation page
if (m_ImageGeometry == isAnImageGeometry)
return;
const BoundingBox::BoundsArrayType &boundsarray = this->GetBoundingBox()->GetBounds();
Point3D originIndex;
FillVector3D(originIndex, boundsarray[0], boundsarray[2], boundsarray[4]);
if (isAnImageGeometry == true)
FillVector3D(originIndex, originIndex[0] + 0.5, originIndex[1] + 0.5, originIndex[2] + 0.5);
else
FillVector3D(originIndex, originIndex[0] - 0.5, originIndex[1] - 0.5, originIndex[2] - 0.5);
Point3D originWorld;
originWorld = GetIndexToWorldTransform()->TransformPoint(originIndex);
// instead could as well call IndexToWorld(originIndex,originWorld);
SetOrigin(originWorld);
this->SetImageGeometry(isAnImageGeometry);
}
void mitk::BaseGeometry::PrintSelf(std::ostream &os, itk::Indent indent) const
{
os << indent << " IndexToWorldTransform: ";
if (this->IsIndexToWorldTransformNull())
os << "nullptr" << std::endl;
else
{
// from itk::MatrixOffsetTransformBase
unsigned int i, j;
os << std::endl;
os << indent << "Matrix: " << std::endl;
for (i = 0; i < 3; i++)
{
os << indent.GetNextIndent();
for (j = 0; j < 3; j++)
{
os << this->GetIndexToWorldTransform()->GetMatrix()[i][j] << " ";
}
os << std::endl;
}
os << indent << "Offset: " << this->GetIndexToWorldTransform()->GetOffset() << std::endl;
os << indent << "Center: " << this->GetIndexToWorldTransform()->GetCenter() << std::endl;
os << indent << "Translation: " << this->GetIndexToWorldTransform()->GetTranslation() << std::endl;
- os << indent << "Inverse: " << std::endl;
- for (i = 0; i < 3; i++)
+ auto inverse = mitk::AffineTransform3D::New();
+ if (this->GetIndexToWorldTransform()->GetInverse(inverse))
{
- os << indent.GetNextIndent();
- for (j = 0; j < 3; j++)
+ os << indent << "Inverse: " << std::endl;
+ for (i = 0; i < 3; i++)
{
- os << this->GetIndexToWorldTransform()->GetInverseMatrix()[i][j] << " ";
+ os << indent.GetNextIndent();
+ for (j = 0; j < 3; j++)
+ {
+ os << inverse->GetMatrix()[i][j] << " ";
+ }
+ os << std::endl;
}
- os << std::endl;
}
// from itk::ScalableAffineTransform
os << indent << "Scale : ";
for (i = 0; i < 3; i++)
{
os << this->GetIndexToWorldTransform()->GetScale()[i] << " ";
}
os << std::endl;
}
os << indent << " BoundingBox: ";
if (this->IsBoundingBoxNull())
os << "nullptr" << std::endl;
else
{
os << indent << "( ";
for (unsigned int i = 0; i < 3; i++)
{
os << this->GetBoundingBox()->GetBounds()[2 * i] << "," << this->GetBoundingBox()->GetBounds()[2 * i + 1] << " ";
}
os << " )" << std::endl;
}
os << indent << " Origin: " << this->GetOrigin() << std::endl;
os << indent << " ImageGeometry: " << this->GetImageGeometry() << std::endl;
os << indent << " Spacing: " << this->GetSpacing() << std::endl;
}
void mitk::BaseGeometry::Modified() const
{
if (!m_ModifiedLockFlag)
Superclass::Modified();
else
m_ModifiedCalledFlag = true;
}
mitk::AffineTransform3D *mitk::BaseGeometry::GetIndexToWorldTransform()
{
return m_GeometryTransform->GetIndexToWorldTransform();
}
const mitk::AffineTransform3D *mitk::BaseGeometry::GetIndexToWorldTransform() const
{
return m_GeometryTransform->GetIndexToWorldTransform();
}
const mitk::GeometryTransformHolder *mitk::BaseGeometry::GetGeometryTransformHolder() const
{
return m_GeometryTransform;
}
bool mitk::Equal(const mitk::BaseGeometry::BoundingBoxType &leftHandSide,
const mitk::BaseGeometry::BoundingBoxType &rightHandSide,
ScalarType eps,
bool verbose)
{
bool result = true;
BaseGeometry::BoundsArrayType rightBounds = rightHandSide.GetBounds();
BaseGeometry::BoundsArrayType leftBounds = leftHandSide.GetBounds();
BaseGeometry::BoundsArrayType::Iterator itLeft = leftBounds.Begin();
for (BaseGeometry::BoundsArrayType::Iterator itRight = rightBounds.Begin(); itRight != rightBounds.End(); ++itRight)
{
if ((!mitk::Equal(*itLeft, *itRight, eps)))
{
if (verbose)
{
MITK_INFO << "[( Geometry3D::BoundingBoxType )] bounds are not equal.";
MITK_INFO << "rightHandSide is " << setprecision(12) << *itRight << " : leftHandSide is " << *itLeft
<< " and tolerance is " << eps;
}
result = false;
}
itLeft++;
}
return result;
}
bool mitk::Equal(const mitk::BaseGeometry &leftHandSide,
const mitk::BaseGeometry &rightHandSide,
ScalarType coordinateEps,
ScalarType directionEps,
bool verbose)
{
bool result = true;
// Compare spacings
if (!mitk::Equal(leftHandSide.GetSpacing(), rightHandSide.GetSpacing(), coordinateEps))
{
if (verbose)
{
MITK_INFO << "[( Geometry3D )] Spacing differs.";
MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetSpacing() << " : leftHandSide is "
<< leftHandSide.GetSpacing() << " and tolerance is " << coordinateEps;
}
result = false;
}
// Compare Origins
if (!mitk::Equal(leftHandSide.GetOrigin(), rightHandSide.GetOrigin(), coordinateEps))
{
if (verbose)
{
MITK_INFO << "[( Geometry3D )] Origin differs.";
MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetOrigin() << " : leftHandSide is "
<< leftHandSide.GetOrigin() << " and tolerance is " << coordinateEps;
}
result = false;
}
// Compare Axis and Extents
for (unsigned int i = 0; i < 3; ++i)
{
if (!mitk::Equal(leftHandSide.GetAxisVector(i), rightHandSide.GetAxisVector(i), directionEps))
{
if (verbose)
{
MITK_INFO << "[( Geometry3D )] AxisVector #" << i << " differ";
MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetAxisVector(i) << " : leftHandSide is "
<< leftHandSide.GetAxisVector(i) << " and tolerance is " << directionEps;
}
result = false;
}
if (!mitk::Equal(leftHandSide.GetExtent(i), rightHandSide.GetExtent(i), coordinateEps))
{
if (verbose)
{
MITK_INFO << "[( Geometry3D )] Extent #" << i << " differ";
MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetExtent(i) << " : leftHandSide is "
<< leftHandSide.GetExtent(i) << " and tolerance is " << coordinateEps;
}
result = false;
}
}
// Compare ImageGeometry Flag
if (rightHandSide.GetImageGeometry() != leftHandSide.GetImageGeometry())
{
if (verbose)
{
MITK_INFO << "[( Geometry3D )] GetImageGeometry is different.";
MITK_INFO << "rightHandSide is " << rightHandSide.GetImageGeometry() << " : leftHandSide is "
<< leftHandSide.GetImageGeometry();
}
result = false;
}
// Compare FrameOfReference ID
if (rightHandSide.GetFrameOfReferenceID() != leftHandSide.GetFrameOfReferenceID())
{
if (verbose)
{
MITK_INFO << "[( Geometry3D )] GetFrameOfReferenceID is different.";
MITK_INFO << "rightHandSide is " << rightHandSide.GetFrameOfReferenceID() << " : leftHandSide is "
<< leftHandSide.GetFrameOfReferenceID();
}
result = false;
}
// Compare BoundingBoxes
if (!mitk::Equal(*leftHandSide.GetBoundingBox(), *rightHandSide.GetBoundingBox(), coordinateEps, verbose))
{
result = false;
}
// Compare IndexToWorldTransform Matrix
if (!mitk::Equal(*leftHandSide.GetIndexToWorldTransform(), *rightHandSide.GetIndexToWorldTransform(), directionEps, verbose))
{
result = false;
}
return result;
}
bool mitk::Equal(const mitk::BaseGeometry& leftHandSide,
const mitk::BaseGeometry& rightHandSide,
ScalarType eps,
bool verbose)
{
return Equal(leftHandSide, rightHandSide, eps, eps, verbose);
}
bool mitk::Equal(const mitk::BaseGeometry::TransformType &leftHandSide,
const mitk::BaseGeometry::TransformType &rightHandSide,
ScalarType eps,
bool verbose)
{
// Compare IndexToWorldTransform Matrix
if (!mitk::MatrixEqualElementWise(leftHandSide.GetMatrix(), rightHandSide.GetMatrix(), eps))
{
if (verbose)
{
MITK_INFO << "[( Geometry3D::TransformType )] Index to World Transformation matrix differs.";
MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetMatrix() << " : leftHandSide is "
<< leftHandSide.GetMatrix() << " and tolerance is " << eps;
}
return false;
}
return true;
}
bool mitk::IsSubGeometry(const mitk::BaseGeometry& testGeo,
const mitk::BaseGeometry& referenceGeo,
ScalarType coordinateEps,
ScalarType directionEps,
bool verbose)
{
bool result = true;
// Compare spacings (must be equal)
const auto testedSpacing = testGeo.GetSpacing();
if (!mitk::Equal(testedSpacing, referenceGeo.GetSpacing(), coordinateEps))
{
if (verbose)
{
MITK_INFO << "[( Geometry3D )] Spacing differs.";
MITK_INFO << "testedGeometry is " << setprecision(12) << testedSpacing << " : referenceGeometry is "
<< referenceGeo.GetSpacing() << " and tolerance is " << coordinateEps;
}
result = false;
}
// Compare ImageGeometry Flag (must be equal)
if (referenceGeo.GetImageGeometry() != testGeo.GetImageGeometry())
{
if (verbose)
{
MITK_INFO << "[( Geometry3D )] GetImageGeometry is different.";
MITK_INFO << "referenceGeo is " << referenceGeo.GetImageGeometry() << " : testGeo is "
<< testGeo.GetImageGeometry();
}
result = false;
}
// Compare IndexToWorldTransform Matrix (must be equal -> same axis directions)
if (!Equal(*(testGeo.GetIndexToWorldTransform()), *(referenceGeo.GetIndexToWorldTransform()), directionEps, verbose))
{
result = false;
}
//check if the geometry is within or equal to the bounds of reference geomentry.
for (int i = 0; i<8; ++i)
{
auto testCorner = testGeo.GetCornerPoint(i);
bool isInside = false;
mitk::Point3D testCornerIndex;
referenceGeo.WorldToIndex(testCorner, testCornerIndex);
std::bitset<sizeof(int)> bs(i);
//To regard the coordinateEps, we substract or add it to the index elements
//depending on wether it was constructed by a lower or an upper bound value
//(see implementation of BaseGeometry::GetCorner()).
if (bs.test(0))
{
testCornerIndex[2] -= coordinateEps;
}
else
{
testCornerIndex[2] += coordinateEps;
}
if (bs.test(1))
{
testCornerIndex[1] -= coordinateEps;
}
else
{
testCornerIndex[1] += coordinateEps;
}
if (bs.test(2))
{
testCornerIndex[0] -= coordinateEps;
}
else
{
testCornerIndex[0] += coordinateEps;
}
isInside = referenceGeo.IsIndexInside(testCornerIndex);
if (!isInside)
{
if (verbose)
{
MITK_INFO << "[( Geometry3D )] corner point is not inside. ";
MITK_INFO << "referenceGeo is " << setprecision(12) << referenceGeo << " : tested corner is "
<< testGeo.GetCornerPoint(i);
}
result = false;
}
}
// check grid of test geometry is on the grid of the reference geometry. This is important as the
// boundingbox is only checked for containing the tested geometry, but if a corner (one is enough
// as we know that axis and spacing are equal, due to equal transfor (see above)) of the tested geometry
// is on the grid it is really a sub geometry (as they have the same spacing and axis).
auto cornerOffset = testGeo.GetCornerPoint(0) - referenceGeo.GetCornerPoint(0);
mitk::Vector3D cornerIndexOffset;
referenceGeo.WorldToIndex(cornerOffset, cornerIndexOffset);
for (unsigned int i = 0; i < 3; ++i)
{
auto pixelCountContinous = cornerIndexOffset[i];
auto pixelCount = std::round(pixelCountContinous);
if (std::abs(pixelCount - pixelCountContinous) > coordinateEps)
{
if (verbose)
{
MITK_INFO << "[( Geometry3D )] Tested geometry is not on the grid of the reference geometry. ";
MITK_INFO << "referenceGeo is " << setprecision(15) << referenceGeo << " : tested corner offset in pixels is "
<< pixelCountContinous << " for axis "<<i;
}
result = false;
}
}
return result;
}
bool mitk::IsSubGeometry(const mitk::BaseGeometry& testGeo,
const mitk::BaseGeometry& referenceGeo,
ScalarType eps,
bool verbose)
{
return IsSubGeometry(testGeo, referenceGeo, eps, eps, verbose);
}
diff --git a/Modules/Core/src/DataManagement/mitkDataNode.cpp b/Modules/Core/src/DataManagement/mitkDataNode.cpp
index c9280edff8..1891969a22 100644
--- a/Modules/Core/src/DataManagement/mitkDataNode.cpp
+++ b/Modules/Core/src/DataManagement/mitkDataNode.cpp
@@ -1,732 +1,737 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkDataNode.h"
#include "mitkCoreObjectFactory.h"
#include <vtkTransform.h>
#include "mitkGroupTagProperty.h"
#include "mitkProperties.h"
#include "mitkSmartPointerProperty.h"
#include "mitkStringProperty.h"
//#include "mitkMaterialProperty.h"
#include "mitkColorProperty.h"
#include "mitkCoreObjectFactory.h"
#include "mitkGenericProperty.h"
#include "mitkGeometry3D.h"
#include "mitkImageSource.h"
#include "mitkLevelWindowProperty.h"
#include "mitkRenderingManager.h"
+namespace mitk
+{
+ itkEventMacroDefinition(InteractorChangedEvent, itk::AnyEvent);
+}
+
mitk::Mapper *mitk::DataNode::GetMapper(MapperSlotId id) const
{
if ((id >= m_Mappers.size()) || (m_Mappers[id].IsNull()))
{
if (id >= m_Mappers.capacity())
{
// int i, size=id-m_Mappers.capacity()+10;
m_Mappers.resize(id + 10);
}
m_Mappers[id] = CoreObjectFactory::GetInstance()->CreateMapper(const_cast<DataNode *>(this), id);
}
return m_Mappers[id];
}
mitk::BaseData *mitk::DataNode::GetData() const
{
return m_Data;
}
void mitk::DataNode::SetData(mitk::BaseData *baseData)
{
if (m_Data != baseData)
{
m_Mappers.clear();
m_Mappers.resize(10);
if (m_Data.IsNotNull() && baseData != nullptr)
{
// Do previous and new data have same type? Keep existing properties.
if (0 == strcmp(m_Data->GetNameOfClass(), baseData->GetNameOfClass()))
{
m_Data = baseData;
}
else
{
m_Data = baseData;
this->GetPropertyList()->Clear();
mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties(this);
}
}
else
{
m_Data = baseData;
mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties(this);
}
m_DataReferenceChangedTime.Modified();
Modified();
}
}
mitk::DataNode::DataNode()
: m_PropertyList(PropertyList::New()),
m_PropertyListModifiedObserverTag(0)
{
m_Mappers.resize(10);
// subscribe for modified event
itk::MemberCommand<mitk::DataNode>::Pointer _PropertyListModifiedCommand = itk::MemberCommand<mitk::DataNode>::New();
_PropertyListModifiedCommand->SetCallbackFunction(this, &mitk::DataNode::PropertyListModified);
m_PropertyListModifiedObserverTag = m_PropertyList->AddObserver(itk::ModifiedEvent(), _PropertyListModifiedCommand);
}
mitk::DataNode::~DataNode()
{
if (m_PropertyList.IsNotNull())
m_PropertyList->RemoveObserver(m_PropertyListModifiedObserverTag);
m_Mappers.clear();
m_Data = nullptr;
}
mitk::DataNode &mitk::DataNode::operator=(const DataNode &right)
{
mitk::DataNode *node = mitk::DataNode::New();
node->SetData(right.GetData());
return *node;
}
mitk::DataNode &mitk::DataNode::operator=(mitk::BaseData *right)
{
mitk::DataNode *node = mitk::DataNode::New();
node->SetData(right);
return *node;
}
#if (_MSC_VER > 1200) || !defined(_MSC_VER)
std::istream &mitk::operator>>(std::istream &i, mitk::DataNode::Pointer &dtn)
#endif
#if ((defined(_MSC_VER)) && (_MSC_VER <= 1200))
std::istream &
operator>>(std::istream &i, mitk::DataNode::Pointer &dtn)
#endif
{
dtn = mitk::DataNode::New();
// i >> av.get();
return i;
}
#if (_MSC_VER > 1200) || !defined(_MSC_VER)
std::ostream &mitk::operator<<(std::ostream &o, mitk::DataNode::Pointer &dtn)
#endif
#if ((defined(_MSC_VER)) && (_MSC_VER <= 1200))
std::ostream &
operator<<(std::ostream &o, mitk::DataNode::Pointer &dtn)
#endif
{
if (dtn->GetData() != nullptr)
o << dtn->GetData()->GetNameOfClass();
else
o << "empty data";
return o;
}
void mitk::DataNode::SetMapper(MapperSlotId id, mitk::Mapper *mapper)
{
m_Mappers[id] = mapper;
if (mapper != nullptr)
mapper->SetDataNode(this);
}
void mitk::DataNode::UpdateOutputInformation()
{
if (this->GetSource())
{
this->GetSource()->UpdateOutputInformation();
}
}
void mitk::DataNode::SetRequestedRegionToLargestPossibleRegion()
{
}
bool mitk::DataNode::RequestedRegionIsOutsideOfTheBufferedRegion()
{
return false;
}
bool mitk::DataNode::VerifyRequestedRegion()
{
return true;
}
void mitk::DataNode::SetRequestedRegion(const itk::DataObject * /*data*/)
{
}
mitk::DataNode::PropertyListKeyNames mitk::DataNode::GetPropertyListNames() const
{
PropertyListKeyNames result;
for (const auto &entries : m_MapOfPropertyLists)
result.push_back(entries.first);
return result;
}
void mitk::DataNode::CopyInformation(const itk::DataObject * /*data*/)
{
}
mitk::PropertyList *mitk::DataNode::GetPropertyList(const mitk::BaseRenderer *renderer) const
{
if (renderer == nullptr)
return m_PropertyList;
return this->GetPropertyList(renderer->GetName());
}
mitk::PropertyList *mitk::DataNode::GetPropertyList(const std::string &rendererName) const
{
if (rendererName.empty())
return m_PropertyList;
mitk::PropertyList::Pointer &propertyList = m_MapOfPropertyLists[rendererName];
if (propertyList.IsNull())
propertyList = mitk::PropertyList::New();
assert(m_MapOfPropertyLists[rendererName].IsNotNull());
return propertyList;
}
void mitk::DataNode::ConcatenatePropertyList(PropertyList *pList, bool replace)
{
m_PropertyList->ConcatenatePropertyList(pList, replace);
}
mitk::BaseProperty *mitk::DataNode::GetProperty(const char *propertyKey, const mitk::BaseRenderer *renderer, bool fallBackOnDataProperties) const
{
if (nullptr == propertyKey)
return nullptr;
if (nullptr != renderer)
{
auto it = m_MapOfPropertyLists.find(renderer->GetName());
if (m_MapOfPropertyLists.end() != it)
{
auto property = it->second->GetProperty(propertyKey);
if (nullptr != property)
return property;
}
}
auto property = m_PropertyList->GetProperty(propertyKey);
if (nullptr == property && fallBackOnDataProperties && m_Data.IsNotNull())
property = m_Data->GetProperty(propertyKey);
return property;
}
mitk::DataNode::GroupTagList mitk::DataNode::GetGroupTags() const
{
GroupTagList groups;
const PropertyList::PropertyMap *propertyMap = m_PropertyList->GetMap();
for (auto groupIter =
propertyMap->begin(); // m_PropertyList is created in the constructor, so we don't check it here
groupIter != propertyMap->end();
++groupIter)
{
const BaseProperty *bp = groupIter->second;
if (dynamic_cast<const GroupTagProperty *>(bp))
{
groups.insert(groupIter->first);
}
}
return groups;
}
bool mitk::DataNode::GetBoolProperty(const char *propertyKey, bool &boolValue, const mitk::BaseRenderer *renderer) const
{
mitk::BoolProperty::Pointer boolprop = dynamic_cast<mitk::BoolProperty *>(GetProperty(propertyKey, renderer));
if (boolprop.IsNull())
return false;
boolValue = boolprop->GetValue();
return true;
}
bool mitk::DataNode::GetIntProperty(const char *propertyKey, int &intValue, const mitk::BaseRenderer *renderer) const
{
mitk::IntProperty::Pointer intprop = dynamic_cast<mitk::IntProperty *>(GetProperty(propertyKey, renderer));
if (intprop.IsNull())
return false;
intValue = intprop->GetValue();
return true;
}
bool mitk::DataNode::GetFloatProperty(const char *propertyKey,
float &floatValue,
const mitk::BaseRenderer *renderer) const
{
mitk::FloatProperty::Pointer floatprop = dynamic_cast<mitk::FloatProperty *>(GetProperty(propertyKey, renderer));
if (floatprop.IsNull())
return false;
floatValue = floatprop->GetValue();
return true;
}
bool mitk::DataNode::GetDoubleProperty(const char *propertyKey,
double &doubleValue,
const mitk::BaseRenderer *renderer) const
{
mitk::DoubleProperty::Pointer doubleprop = dynamic_cast<mitk::DoubleProperty *>(GetProperty(propertyKey, renderer));
if (doubleprop.IsNull())
{
// try float instead
float floatValue = 0;
if (this->GetFloatProperty(propertyKey, floatValue, renderer))
{
doubleValue = floatValue;
return true;
}
return false;
}
doubleValue = doubleprop->GetValue();
return true;
}
bool mitk::DataNode::GetStringProperty(const char *propertyKey,
std::string &string,
const mitk::BaseRenderer *renderer) const
{
mitk::StringProperty::Pointer stringProp = dynamic_cast<mitk::StringProperty *>(GetProperty(propertyKey, renderer));
if (stringProp.IsNull())
{
return false;
}
else
{
// memcpy((void*)string, stringProp->GetValue(), strlen(stringProp->GetValue()) + 1 ); // looks dangerous
string = stringProp->GetValue();
return true;
}
}
bool mitk::DataNode::GetColor(float rgb[3], const mitk::BaseRenderer *renderer, const char *propertyKey) const
{
mitk::ColorProperty::Pointer colorprop = dynamic_cast<mitk::ColorProperty *>(GetProperty(propertyKey, renderer));
if (colorprop.IsNull())
return false;
memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float));
return true;
}
bool mitk::DataNode::GetOpacity(float &opacity, const mitk::BaseRenderer *renderer, const char *propertyKey) const
{
mitk::FloatProperty::Pointer opacityprop = dynamic_cast<mitk::FloatProperty *>(GetProperty(propertyKey, renderer));
if (opacityprop.IsNull())
return false;
opacity = opacityprop->GetValue();
return true;
}
bool mitk::DataNode::GetLevelWindow(mitk::LevelWindow &levelWindow,
const mitk::BaseRenderer *renderer,
const char *propertyKey) const
{
mitk::LevelWindowProperty::Pointer levWinProp =
dynamic_cast<mitk::LevelWindowProperty *>(GetProperty(propertyKey, renderer));
if (levWinProp.IsNull())
return false;
levelWindow = levWinProp->GetLevelWindow();
return true;
}
void mitk::DataNode::SetColor(const mitk::Color &color, const mitk::BaseRenderer *renderer, const char *propertyKey)
{
mitk::ColorProperty::Pointer prop;
prop = mitk::ColorProperty::New(color);
GetPropertyList(renderer)->SetProperty(propertyKey, prop);
}
void mitk::DataNode::SetColor(
float red, float green, float blue, const mitk::BaseRenderer *renderer, const char *propertyKey)
{
float color[3];
color[0] = red;
color[1] = green;
color[2] = blue;
SetColor(color, renderer, propertyKey);
}
void mitk::DataNode::SetColor(const float rgb[3], const mitk::BaseRenderer *renderer, const char *propertyKey)
{
mitk::ColorProperty::Pointer prop;
prop = mitk::ColorProperty::New(rgb);
GetPropertyList(renderer)->SetProperty(propertyKey, prop);
}
void mitk::DataNode::SetVisibility(bool visible, const mitk::BaseRenderer *renderer, const char *propertyKey)
{
mitk::BoolProperty::Pointer prop;
prop = mitk::BoolProperty::New(visible);
GetPropertyList(renderer)->SetProperty(propertyKey, prop);
}
void mitk::DataNode::SetOpacity(float opacity, const mitk::BaseRenderer *renderer, const char *propertyKey)
{
mitk::FloatProperty::Pointer prop;
prop = mitk::FloatProperty::New(opacity);
GetPropertyList(renderer)->SetProperty(propertyKey, prop);
}
void mitk::DataNode::SetLevelWindow(mitk::LevelWindow levelWindow,
const mitk::BaseRenderer *renderer,
const char *propertyKey)
{
mitk::LevelWindowProperty::Pointer prop;
prop = mitk::LevelWindowProperty::New(levelWindow);
GetPropertyList(renderer)->SetProperty(propertyKey, prop);
}
void mitk::DataNode::SetIntProperty(const char *propertyKey, int intValue, const mitk::BaseRenderer *renderer)
{
GetPropertyList(renderer)->SetProperty(propertyKey, mitk::IntProperty::New(intValue));
}
void mitk::DataNode::SetBoolProperty(const char *propertyKey,
bool boolValue,
const mitk::BaseRenderer *renderer /*=nullptr*/)
{
GetPropertyList(renderer)->SetProperty(propertyKey, mitk::BoolProperty::New(boolValue));
}
void mitk::DataNode::SetFloatProperty(const char *propertyKey,
float floatValue,
const mitk::BaseRenderer *renderer /*=nullptr*/)
{
if (dynamic_cast<DoubleProperty *>(this->GetProperty(propertyKey, renderer)) != nullptr)
{
MITK_WARN << "Setting float property " << propertyKey
<< " although a double property with the same name already exists";
}
GetPropertyList(renderer)->SetProperty(propertyKey, mitk::FloatProperty::New(floatValue));
}
void mitk::DataNode::SetDoubleProperty(const char *propertyKey, double doubleValue, const mitk::BaseRenderer *renderer)
{
if (dynamic_cast<FloatProperty *>(this->GetProperty(propertyKey, renderer)) != nullptr)
{
MITK_WARN << "Setting double property " << propertyKey
<< " although a float property with the same name already exists";
}
GetPropertyList(renderer)->SetProperty(propertyKey, mitk::DoubleProperty::New(doubleValue));
}
void mitk::DataNode::SetStringProperty(const char *propertyKey,
const char *stringValue,
const mitk::BaseRenderer *renderer /*=nullptr*/)
{
GetPropertyList(renderer)->SetProperty(propertyKey, mitk::StringProperty::New(stringValue));
}
void mitk::DataNode::SetProperty(const char *propertyKey,
BaseProperty *propertyValue,
const mitk::BaseRenderer *renderer)
{
GetPropertyList(renderer)->SetProperty(propertyKey, propertyValue);
}
void mitk::DataNode::ReplaceProperty(const char *propertyKey,
BaseProperty *propertyValue,
const mitk::BaseRenderer *renderer)
{
GetPropertyList(renderer)->ReplaceProperty(propertyKey, propertyValue);
}
void mitk::DataNode::AddProperty(const char *propertyKey,
BaseProperty *propertyValue,
const mitk::BaseRenderer *renderer,
bool overwrite)
{
if ((overwrite) || (GetProperty(propertyKey, renderer) == nullptr))
{
SetProperty(propertyKey, propertyValue, renderer);
}
}
vtkLinearTransform *mitk::DataNode::GetVtkTransform(int t) const
{
assert(m_Data.IsNotNull());
mitk::BaseGeometry *geometry = m_Data->GetGeometry(t);
if (geometry == nullptr)
return nullptr;
return geometry->GetVtkTransform();
}
-unsigned long mitk::DataNode::GetMTime() const
+itk::ModifiedTimeType mitk::DataNode::GetMTime() const
{
- unsigned long time = Superclass::GetMTime();
+ auto time = Superclass::GetMTime();
if (m_Data.IsNotNull())
{
if ((time < m_Data->GetMTime()) || ((m_Data->GetSource().IsNotNull()) && (time < m_Data->GetSource()->GetMTime())))
{
Modified();
return Superclass::GetMTime();
}
}
return time;
}
void mitk::DataNode::SetSelected(bool selected, const mitk::BaseRenderer *renderer)
{
mitk::BoolProperty::Pointer selectedProperty = dynamic_cast<mitk::BoolProperty *>(GetProperty("selected"));
if (selectedProperty.IsNull())
{
selectedProperty = mitk::BoolProperty::New();
selectedProperty->SetValue(false);
SetProperty("selected", selectedProperty, renderer);
}
if (selectedProperty->GetValue() != selected)
{
selectedProperty->SetValue(selected);
itk::ModifiedEvent event;
InvokeEvent(event);
}
}
/*
class SelectedEvent : public itk::ModifiedEvent
{
public:
typedef SelectedEvent Self;
typedef itk::ModifiedEvent Superclass;
SelectedEvent(DataNode* dataNode)
{ m_DataNode = dataNode; };
DataNode* GetDataNode()
{ return m_DataNode; };
virtual const char * GetEventName() const
{ return "SelectedEvent"; }
virtual bool CheckEvent(const ::itk::EventObject* e) const
{ return dynamic_cast<const Self*>(e); }
virtual ::itk::EventObject* MakeObject() const
{ return new Self(m_DataNode); }
private:
DataNode* m_DataNode;
SelectedEvent(const Self& event)
{ m_DataNode = event.m_DataNode; };
void operator=(const Self& event)
{ m_DataNode = event.m_DataNode; }
};
*/
bool mitk::DataNode::IsSelected(const mitk::BaseRenderer *renderer)
{
bool selected;
if (!GetBoolProperty("selected", selected, renderer))
return false;
return selected;
}
void mitk::DataNode::SetDataInteractor(const DataInteractor::Pointer interactor)
{
if (m_DataInteractor == interactor)
return;
m_DataInteractor = interactor;
this->Modified();
- mitk::DataNode::InteractorChangedEvent changedEvent;
+ InteractorChangedEvent changedEvent;
this->InvokeEvent(changedEvent);
}
mitk::DataInteractor::Pointer mitk::DataNode::GetDataInteractor() const
{
return m_DataInteractor;
}
void mitk::DataNode::PropertyListModified(const itk::Object * /*caller*/, const itk::EventObject &)
{
Modified();
}
mitk::BaseProperty::ConstPointer mitk::DataNode::GetConstProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext) const
{
if (propertyKey.empty())
return nullptr;
if (!contextName.empty())
{
auto propertyListIter = m_MapOfPropertyLists.find(contextName);
if (m_MapOfPropertyLists.end() != propertyListIter)
{
BaseProperty::ConstPointer property = propertyListIter->second->GetProperty(propertyKey);
if (property.IsNotNull())
return property;
}
}
if (contextName.empty() || fallBackOnDefaultContext)
{
BaseProperty::ConstPointer property = m_PropertyList->GetProperty(propertyKey);
if (property.IsNull() && m_Data.IsNotNull())
property = m_Data->GetProperty(propertyKey.c_str());
return property;
}
return nullptr;
}
mitk::BaseProperty * mitk::DataNode::GetNonConstProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext)
{
if (propertyKey.empty())
return nullptr;
if (!contextName.empty())
{
auto propertyListIter = m_MapOfPropertyLists.find(contextName);
if (m_MapOfPropertyLists.end() != propertyListIter)
{
auto property = propertyListIter->second->GetProperty(propertyKey);
if (nullptr != property)
return property;
}
}
if (contextName.empty() || fallBackOnDefaultContext)
{
auto property = m_PropertyList->GetProperty(propertyKey);
if (nullptr == property && m_Data.IsNotNull())
property = m_Data->GetProperty(propertyKey.c_str());
return property;
}
return nullptr;
}
void mitk::DataNode::SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName, bool fallBackOnDefaultContext)
{
if (propertyKey.empty())
mitkThrow() << "Property key is empty.";
if (!contextName.empty())
{
auto propertyListIter = m_MapOfPropertyLists.find(contextName);
if (m_MapOfPropertyLists.end() != propertyListIter)
{
propertyListIter->second->SetProperty(propertyKey, property);
return;
}
}
if (contextName.empty() || fallBackOnDefaultContext)
{
m_PropertyList->SetProperty(propertyKey, property);
return;
}
mitkThrow() << "Unknown property context.";
}
void mitk::DataNode::RemoveProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext)
{
if (propertyKey.empty())
mitkThrow() << "Property key is empty.";
if (!contextName.empty())
{
auto propertyListIter = m_MapOfPropertyLists.find(contextName);
if (m_MapOfPropertyLists.end() != propertyListIter)
{
propertyListIter->second->RemoveProperty(propertyKey);
return;
}
}
if (contextName.empty() || fallBackOnDefaultContext)
{
m_PropertyList->RemoveProperty(propertyKey);
return;
}
mitkThrow() << "Unknown property context.";
}
std::vector<std::string> mitk::DataNode::GetPropertyKeys(const std::string &contextName, bool includeDefaultContext) const
{
std::vector<std::string> propertyKeys;
if (contextName.empty())
{
for (const auto &property : *m_PropertyList->GetMap())
propertyKeys.push_back(property.first);
return propertyKeys;
}
auto propertyListIter = m_MapOfPropertyLists.find(contextName);
if (m_MapOfPropertyLists.end() != propertyListIter)
{
for (const auto &property : *propertyListIter->second->GetMap())
propertyKeys.push_back(property.first);
}
if (includeDefaultContext)
{
for (const auto &property : *m_PropertyList->GetMap())
{
auto propertyKeyIter = std::find(propertyKeys.begin(), propertyKeys.end(), property.first);
if (propertyKeys.end() == propertyKeyIter)
propertyKeys.push_back(property.first);
}
}
return propertyKeys;
}
std::vector<std::string> mitk::DataNode::GetPropertyContextNames() const
{
return this->GetPropertyListNames();
}
diff --git a/Modules/Core/src/DataManagement/mitkDataStorage.cpp b/Modules/Core/src/DataManagement/mitkDataStorage.cpp
index 5a555a15da..894407d29e 100644
--- a/Modules/Core/src/DataManagement/mitkDataStorage.cpp
+++ b/Modules/Core/src/DataManagement/mitkDataStorage.cpp
@@ -1,584 +1,583 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkDataStorage.h"
#include "itkCommand.h"
-#include "itkMutexLockHolder.h"
#include "mitkDataNode.h"
#include "mitkGroupTagProperty.h"
#include "mitkImage.h"
#include "mitkNodePredicateBase.h"
#include "mitkNodePredicateProperty.h"
#include "mitkProperties.h"
#include "mitkArbitraryTimeGeometry.h"
mitk::DataStorage::DataStorage() : itk::Object(), m_BlockNodeModifiedEvents(false)
{
}
mitk::DataStorage::~DataStorage()
{
///// we can not call GetAll() in destructor, because it is implemented in a subclass
// SetOfObjects::ConstPointer all = this->GetAll();
// for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it)
// this->RemoveListeners(it->Value());
// m_NodeModifiedObserverTags.clear();
// m_NodeDeleteObserverTags.clear();
}
void mitk::DataStorage::Add(DataNode *node, DataNode *parent)
{
DataStorage::SetOfObjects::Pointer parents = DataStorage::SetOfObjects::New();
if (parent != nullptr) //< Return empty set if parent is null
parents->InsertElement(0, parent);
this->Add(node, parents);
}
void mitk::DataStorage::Remove(const DataStorage::SetOfObjects *nodes)
{
if (nodes == nullptr)
return;
for (DataStorage::SetOfObjects::ConstIterator it = nodes->Begin(); it != nodes->End(); it++)
this->Remove(it.Value());
}
mitk::DataStorage::SetOfObjects::ConstPointer mitk::DataStorage::GetSubset(const NodePredicateBase *condition) const
{
DataStorage::SetOfObjects::ConstPointer result = this->FilterSetOfObjects(this->GetAll(), condition);
return result;
}
mitk::DataNode *mitk::DataStorage::GetNamedNode(const char *name) const
{
if (name == nullptr)
return nullptr;
StringProperty::Pointer s(StringProperty::New(name));
NodePredicateProperty::Pointer p = NodePredicateProperty::New("name", s);
DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(p);
if (rs->Size() >= 1)
return rs->GetElement(0);
else
return nullptr;
}
mitk::DataNode *mitk::DataStorage::GetNode(const NodePredicateBase *condition) const
{
if (condition == nullptr)
return nullptr;
DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(condition);
if (rs->Size() >= 1)
return rs->GetElement(0);
else
return nullptr;
}
mitk::DataNode *mitk::DataStorage::GetNamedDerivedNode(const char *name,
const DataNode *sourceNode,
bool onlyDirectDerivations) const
{
if (name == nullptr)
return nullptr;
StringProperty::Pointer s(StringProperty::New(name));
NodePredicateProperty::Pointer p = NodePredicateProperty::New("name", s);
DataStorage::SetOfObjects::ConstPointer rs = this->GetDerivations(sourceNode, p, onlyDirectDerivations);
if (rs->Size() >= 1)
return rs->GetElement(0);
else
return nullptr;
}
void mitk::DataStorage::PrintSelf(std::ostream &os, itk::Indent indent) const
{
// Superclass::PrintSelf(os, indent);
DataStorage::SetOfObjects::ConstPointer all = this->GetAll();
os << indent << "DataStorage " << this << " is managing " << all->Size() << " objects. List of objects:" << std::endl;
for (DataStorage::SetOfObjects::ConstIterator allIt = all->Begin(); allIt != all->End(); allIt++)
{
std::string name;
allIt.Value()->GetName(name);
std::string datatype;
if (allIt.Value()->GetData() != nullptr)
datatype = allIt.Value()->GetData()->GetNameOfClass();
os << indent << " " << allIt.Value().GetPointer() << "<" << datatype << ">: " << name << std::endl;
DataStorage::SetOfObjects::ConstPointer parents = this->GetSources(allIt.Value());
if (parents->Size() > 0)
{
os << indent << " Direct sources: ";
for (DataStorage::SetOfObjects::ConstIterator parentIt = parents->Begin(); parentIt != parents->End();
parentIt++)
os << parentIt.Value().GetPointer() << ", ";
os << std::endl;
}
DataStorage::SetOfObjects::ConstPointer derivations = this->GetDerivations(allIt.Value());
if (derivations->Size() > 0)
{
os << indent << " Direct derivations: ";
for (DataStorage::SetOfObjects::ConstIterator derivationIt = derivations->Begin();
derivationIt != derivations->End();
derivationIt++)
os << derivationIt.Value().GetPointer() << ", ";
os << std::endl;
}
}
os << std::endl;
}
mitk::DataStorage::SetOfObjects::ConstPointer mitk::DataStorage::FilterSetOfObjects(const SetOfObjects *set,
const NodePredicateBase *condition) const
{
if (set == nullptr)
return nullptr;
DataStorage::SetOfObjects::Pointer result = DataStorage::SetOfObjects::New();
for (DataStorage::SetOfObjects::ConstIterator it = set->Begin(); it != set->End(); it++)
if (condition == nullptr ||
condition->CheckNode(it.Value()) ==
true) // alway copy the set, otherwise the iterator in DataStorage::Remove() will crash
result->InsertElement(result->Size(), it.Value());
return DataStorage::SetOfObjects::ConstPointer(result);
}
const mitk::DataNode::GroupTagList mitk::DataStorage::GetGroupTags() const
{
DataNode::GroupTagList result;
SetOfObjects::ConstPointer all = this->GetAll();
if (all.IsNull())
return result;
for (DataStorage::SetOfObjects::ConstIterator nodeIt = all->Begin(); nodeIt != all->End();
nodeIt++) // for each node
{
PropertyList *pl = nodeIt.Value()->GetPropertyList();
for (auto propIt = pl->GetMap()->begin(); propIt != pl->GetMap()->end();
++propIt)
if (dynamic_cast<GroupTagProperty *>(propIt->second.GetPointer()) != nullptr)
result.insert(propIt->first);
}
return result;
}
void mitk::DataStorage::EmitAddNodeEvent(const DataNode *node)
{
AddNodeEvent.Send(node);
}
void mitk::DataStorage::EmitRemoveNodeEvent(const DataNode *node)
{
RemoveNodeEvent.Send(node);
}
void mitk::DataStorage::OnNodeInteractorChanged(itk::Object *caller, const itk::EventObject &)
{
const auto *_Node = dynamic_cast<const DataNode *>(caller);
if (_Node)
{
InteractorChangedNodeEvent.Send(_Node);
}
}
void mitk::DataStorage::OnNodeModifiedOrDeleted(const itk::Object *caller, const itk::EventObject &event)
{
if (m_BlockNodeModifiedEvents)
return;
const auto *_Node = dynamic_cast<const DataNode *>(caller);
if (_Node)
{
const auto *modEvent = dynamic_cast<const itk::ModifiedEvent *>(&event);
if (modEvent)
ChangedNodeEvent.Send(_Node);
else
DeleteNodeEvent.Send(_Node);
}
}
void mitk::DataStorage::AddListeners(const DataNode *_Node)
{
- itk::MutexLockHolder<itk::SimpleFastMutexLock> locked(m_MutexOne);
+ std::lock_guard<std::mutex> locked(m_MutexOne);
// node must not be 0 and must not be yet registered
auto *NonConstNode = const_cast<DataNode *>(_Node);
if (_Node && m_NodeModifiedObserverTags.find(NonConstNode) == m_NodeModifiedObserverTags.end())
{
itk::MemberCommand<DataStorage>::Pointer nodeModifiedCommand = itk::MemberCommand<DataStorage>::New();
nodeModifiedCommand->SetCallbackFunction(this, &DataStorage::OnNodeModifiedOrDeleted);
m_NodeModifiedObserverTags[NonConstNode] = NonConstNode->AddObserver(itk::ModifiedEvent(), nodeModifiedCommand);
itk::MemberCommand<DataStorage>::Pointer interactorChangedCommand =
itk::MemberCommand<DataStorage>::New();
interactorChangedCommand->SetCallbackFunction(this, &DataStorage::OnNodeInteractorChanged);
m_NodeInteractorChangedObserverTags[NonConstNode] =
- NonConstNode->AddObserver(DataNode::InteractorChangedEvent(), interactorChangedCommand);
+ NonConstNode->AddObserver(InteractorChangedEvent(), interactorChangedCommand);
// add itk delete listener on datastorage
itk::MemberCommand<DataStorage>::Pointer deleteCommand = itk::MemberCommand<DataStorage>::New();
deleteCommand->SetCallbackFunction(this, &DataStorage::OnNodeModifiedOrDeleted);
// add observer
m_NodeDeleteObserverTags[NonConstNode] = NonConstNode->AddObserver(itk::DeleteEvent(), deleteCommand);
}
}
void mitk::DataStorage::RemoveListeners(const DataNode *_Node)
{
- itk::MutexLockHolder<itk::SimpleFastMutexLock> locked(m_MutexOne);
+ std::lock_guard<std::mutex> locked(m_MutexOne);
// node must not be 0 and must be registered
auto *NonConstNode = const_cast<DataNode *>(_Node);
if (_Node && m_NodeModifiedObserverTags.find(NonConstNode) != m_NodeModifiedObserverTags.end())
{
// const cast is bad! but sometimes it is necessary. removing an observer does not really
// touch the internal state
NonConstNode->RemoveObserver(m_NodeModifiedObserverTags.find(NonConstNode)->second);
NonConstNode->RemoveObserver(m_NodeDeleteObserverTags.find(NonConstNode)->second);
NonConstNode->RemoveObserver(m_NodeInteractorChangedObserverTags.find(NonConstNode)->second);
m_NodeModifiedObserverTags.erase(NonConstNode);
m_NodeDeleteObserverTags.erase(NonConstNode);
m_NodeInteractorChangedObserverTags.erase(NonConstNode);
}
}
mitk::TimeGeometry::ConstPointer mitk::DataStorage::ComputeBoundingGeometry3D(const SetOfObjects *input,
const char *boolPropertyKey,
const BaseRenderer *renderer,
const char *boolPropertyKey2) const
{
if (input == nullptr)
throw std::invalid_argument("DataStorage: input is invalid");
BoundingBox::PointsContainer::Pointer pointscontainer = BoundingBox::PointsContainer::New();
BoundingBox::PointIdentifier pointid = 0;
Point3D point;
Vector3D minSpacing;
minSpacing.Fill(itk::NumericTraits<ScalarType>::max());
ScalarType stmax = itk::NumericTraits<ScalarType>::max();
ScalarType stmin = itk::NumericTraits<ScalarType>::NonpositiveMin();
std::set<ScalarType> existingTimePoints;
ScalarType maximalTime = 0;
// Needed for check of zero bounding boxes
ScalarType nullpoint[] = {0, 0, 0, 0, 0, 0};
BoundingBox::BoundsArrayType itkBoundsZero(nullpoint);
for (SetOfObjects::ConstIterator it = input->Begin(); it != input->End(); ++it)
{
DataNode::Pointer node = it->Value();
if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) &&
node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer))
{
const TimeGeometry *timeGeometry = node->GetData()->GetUpdatedTimeGeometry();
if (timeGeometry != nullptr)
{
// bounding box (only if non-zero)
BoundingBox::BoundsArrayType itkBounds = timeGeometry->GetBoundingBoxInWorld()->GetBounds();
if (itkBounds == itkBoundsZero)
{
continue;
}
unsigned char i;
for (i = 0; i < 8; ++i)
{
point = timeGeometry->GetCornerPointInWorld(i);
if (point[0] * point[0] + point[1] * point[1] + point[2] * point[2] < large)
pointscontainer->InsertElement(pointid++, point);
else
{
itkGenericOutputMacro(<< "Unrealistically distant corner point encountered. Ignored. Node: " << node);
}
}
try
{
// time bounds
// iterate over all time steps
// Attention: Objects with zero bounding box are not respected in time bound calculation
for (TimeStepType i = 0; i < timeGeometry->CountTimeSteps(); i++)
{
// We must not use 'node->GetData()->GetGeometry(i)->GetSpacing()' here, as it returns the spacing
// in its original space, which, in case of an image geometry, can have the values in different
// order than in world space. For the further calculations, we need to have the spacing values
// in world coordinate order (sag-cor-ax).
Vector3D spacing;
spacing.Fill(1.0);
node->GetData()->GetGeometry(i)->IndexToWorld(spacing, spacing);
for (int axis = 0; axis < 3; ++ axis)
{
ScalarType space = std::abs(spacing[axis]);
if (space < minSpacing[axis])
{
minSpacing[axis] = space;
}
}
const auto curTimeBounds = timeGeometry->GetTimeBounds(i);
if ((curTimeBounds[0] > stmin) && (curTimeBounds[0] < stmax))
{
existingTimePoints.insert(curTimeBounds[0]);
}
if ((curTimeBounds[1] > maximalTime) && (curTimeBounds[1] < stmax))
{
maximalTime = curTimeBounds[1];
}
}
}
catch ( const itk::ExceptionObject &e )
{
MITK_ERROR << e.GetDescription() << std::endl;
}
}
}
}
BoundingBox::Pointer result = BoundingBox::New();
result->SetPoints(pointscontainer);
result->ComputeBoundingBox();
// compute the number of time steps
if (existingTimePoints.empty()) // make sure that there is at least one time sliced geometry in the data storage
{
existingTimePoints.insert(0.0);
maximalTime = 1.0;
}
ArbitraryTimeGeometry::Pointer timeGeometry = nullptr;
if (result->GetPoints()->Size() > 0)
{
// Initialize a geometry of a single time step
Geometry3D::Pointer geometry = Geometry3D::New();
geometry->Initialize();
// correct bounding-box (is now in mm, should be in index-coordinates)
// according to spacing
BoundingBox::BoundsArrayType bounds = result->GetBounds();
AffineTransform3D::OutputVectorType offset;
for (int i = 0; i < 3; ++i)
{
offset[i] = bounds[i * 2];
bounds[i * 2] = 0.0;
bounds[i * 2 + 1] = (bounds[i * 2 + 1] - offset[i]) / minSpacing[i];
}
geometry->GetIndexToWorldTransform()->SetOffset(offset);
geometry->SetBounds(bounds);
geometry->SetSpacing(minSpacing);
// Initialize the time sliced geometry
auto tsIterator = existingTimePoints.cbegin();
auto tsPredecessor = tsIterator++;
auto tsEnd = existingTimePoints.cend();
timeGeometry = ArbitraryTimeGeometry::New();
for (; tsIterator != tsEnd; ++tsIterator, ++tsPredecessor)
{
timeGeometry->AppendNewTimeStep(geometry, *tsPredecessor, *tsIterator);
}
timeGeometry->AppendNewTimeStep(geometry, *tsPredecessor, maximalTime);
timeGeometry->Update();
}
return timeGeometry.GetPointer();
}
mitk::TimeGeometry::ConstPointer mitk::DataStorage::ComputeBoundingGeometry3D(const char *boolPropertyKey,
const BaseRenderer *renderer,
const char *boolPropertyKey2) const
{
return this->ComputeBoundingGeometry3D(this->GetAll(), boolPropertyKey, renderer, boolPropertyKey2);
}
mitk::TimeGeometry::ConstPointer mitk::DataStorage::ComputeVisibleBoundingGeometry3D(const BaseRenderer *renderer,
const char *boolPropertyKey)
{
return ComputeBoundingGeometry3D("visible", renderer, boolPropertyKey);
}
mitk::BoundingBox::Pointer mitk::DataStorage::ComputeBoundingBox(const char *boolPropertyKey,
const BaseRenderer *renderer,
const char *boolPropertyKey2)
{
BoundingBox::PointsContainer::Pointer pointscontainer = BoundingBox::PointsContainer::New();
BoundingBox::PointIdentifier pointid = 0;
Point3D point;
// Needed for check of zero bounding boxes
ScalarType nullpoint[] = {0, 0, 0, 0, 0, 0};
BoundingBox::BoundsArrayType itkBoundsZero(nullpoint);
SetOfObjects::ConstPointer all = this->GetAll();
for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it)
{
DataNode::Pointer node = it->Value();
if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) &&
node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer))
{
const TimeGeometry *geometry = node->GetData()->GetUpdatedTimeGeometry();
if (geometry != nullptr)
{
// bounding box (only if non-zero)
BoundingBox::BoundsArrayType itkBounds = geometry->GetBoundingBoxInWorld()->GetBounds();
if (itkBounds == itkBoundsZero)
{
continue;
}
unsigned char i;
for (i = 0; i < 8; ++i)
{
point = geometry->GetCornerPointInWorld(i);
if (point[0] * point[0] + point[1] * point[1] + point[2] * point[2] < large)
pointscontainer->InsertElement(pointid++, point);
else
{
itkGenericOutputMacro(<< "Unrealistically distant corner point encountered. Ignored. Node: " << node);
}
}
}
}
}
BoundingBox::Pointer result = BoundingBox::New();
result->SetPoints(pointscontainer);
result->ComputeBoundingBox();
return result;
}
mitk::TimeBounds mitk::DataStorage::ComputeTimeBounds(const char *boolPropertyKey,
const BaseRenderer *renderer,
const char *boolPropertyKey2)
{
TimeBounds timeBounds;
ScalarType stmin, stmax, cur;
stmin = itk::NumericTraits<ScalarType>::NonpositiveMin();
stmax = itk::NumericTraits<ScalarType>::max();
timeBounds[0] = stmax;
timeBounds[1] = stmin;
SetOfObjects::ConstPointer all = this->GetAll();
for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it)
{
DataNode::Pointer node = it->Value();
if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) &&
node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer))
{
const TimeGeometry *geometry = node->GetData()->GetUpdatedTimeGeometry();
if (geometry != nullptr)
{
const TimeBounds &curTimeBounds = geometry->GetTimeBounds();
cur = curTimeBounds[0];
// is it after -infinity, but before everything else that we found until now?
if ((cur > stmin) && (cur < timeBounds[0]))
timeBounds[0] = cur;
cur = curTimeBounds[1];
// is it before infinity, but after everything else that we found until now?
if ((cur < stmax) && (cur > timeBounds[1]))
timeBounds[1] = cur;
}
}
}
if (!(timeBounds[0] < stmax))
{
timeBounds[0] = stmin;
timeBounds[1] = stmax;
}
return timeBounds;
}
void mitk::DataStorage::BlockNodeModifiedEvents(bool block)
{
m_BlockNodeModifiedEvents = block;
}
mitk::DataNode::Pointer mitk::FindTopmostVisibleNode(const DataStorage::SetOfObjects::ConstPointer nodes,
const Point3D worldPosition,
const TimePointType timePoint,
const BaseRenderer* baseRender)
{
if (nodes.IsNull())
{
return nullptr;
}
mitk::DataNode::Pointer topLayerNode = nullptr;
int maxLayer = std::numeric_limits<int>::min();
for (const auto &node : *nodes)
{
if (node.IsNull())
{
continue;
}
bool isHelperObject = false;
node->GetBoolProperty("helper object", isHelperObject);
if (isHelperObject)
{
continue;
}
auto data = node->GetData();
if (nullptr == data)
{
continue;
}
auto geometry = data->GetGeometry();
if (nullptr == geometry || !geometry->IsInside(worldPosition))
{
continue;
}
auto timeGeometry = data->GetUpdatedTimeGeometry();
if (nullptr == timeGeometry)
{
continue;
}
if (!timeGeometry->IsValidTimePoint(timePoint))
{
continue;
}
int layer = 0;
if (!node->GetIntProperty("layer", layer, baseRender))
{
continue;
}
if (layer <= maxLayer)
{
continue;
}
if (!node->IsVisible(baseRender))
{
continue;
}
topLayerNode = node;
maxLayer = layer;
}
return topLayerNode;
}
diff --git a/Modules/Core/src/DataManagement/mitkGeometryTransformHolder.cpp b/Modules/Core/src/DataManagement/mitkGeometryTransformHolder.cpp
index a20204ef84..3ee34c7cf6 100644
--- a/Modules/Core/src/DataManagement/mitkGeometryTransformHolder.cpp
+++ b/Modules/Core/src/DataManagement/mitkGeometryTransformHolder.cpp
@@ -1,335 +1,335 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
/*
* mitkGeometryTransformHolder.cpp
*
* Created on: Sep 3, 2014
* Author: wirkert
*/
#include <cassert>
#include <itkMatrix.h>
#include <itkScalableAffineTransform.h>
#include <itkSmartPointer.h>
#include <mitkBaseGeometry.h>
#include <mitkGeometryTransformHolder.h>
#include <mitkMatrix.h>
#include <mitkMatrixConvert.h>
#include <mitkPoint.h>
#include <mitkVector.h>
#include <vnl/vnl_matrix_fixed.h>
#include <vnl/vnl_vector.h>
#include <vnl/vnl_vector_fixed.h>
#include <vtkMatrix4x4.h>
#include <vtkMatrixToLinearTransform.h>
#include <vtkTransform.h>
namespace mitk
{
void GeometryTransformHolder::CopySpacingFromTransform(const mitk::AffineTransform3D *transform,
mitk::Vector3D &spacing)
{
mitk::AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix;
vnlmatrix = transform->GetMatrix().GetVnlMatrix();
spacing[0] = vnlmatrix.get_column(0).magnitude();
spacing[1] = vnlmatrix.get_column(1).magnitude();
spacing[2] = vnlmatrix.get_column(2).magnitude();
}
GeometryTransformHolder::GeometryTransformHolder()
{
m_VtkMatrix = vtkMatrix4x4::New();
m_VtkIndexToWorldTransform = vtkMatrixToLinearTransform::New();
m_VtkIndexToWorldTransform->SetInput(m_VtkMatrix);
this->Initialize();
}
GeometryTransformHolder::GeometryTransformHolder(const GeometryTransformHolder &other)
{
m_VtkMatrix = vtkMatrix4x4::New();
m_VtkIndexToWorldTransform = vtkMatrixToLinearTransform::New();
m_VtkIndexToWorldTransform->SetInput(m_VtkMatrix);
this->Initialize(&other);
}
GeometryTransformHolder::~GeometryTransformHolder()
{
m_VtkMatrix->Delete();
m_VtkIndexToWorldTransform->Delete();
}
void GeometryTransformHolder::Initialize()
{
if (m_IndexToWorldTransform.IsNull())
m_IndexToWorldTransform = TransformType::New();
else
m_IndexToWorldTransform->SetIdentity();
m_VtkMatrix->Identity();
}
void GeometryTransformHolder::Initialize(const GeometryTransformHolder *other)
{
Initialize();
if (other->GetIndexToWorldTransform())
{
TransformType::Pointer indexToWorldTransform = other->GetIndexToWorldTransform()->Clone();
this->SetIndexToWorldTransform(indexToWorldTransform);
}
}
//##Documentation
//## @brief Copy the ITK transform
//## (m_IndexToWorldTransform) to the VTK transform
//## \sa SetIndexToWorldTransform
void GeometryTransformHolder::TransferItkToVtkTransform()
{
TransferItkTransformToVtkMatrix(m_IndexToWorldTransform.GetPointer(), m_VtkMatrix);
}
//##Documentation
//## @brief Copy the VTK transform
//## to the ITK transform (m_IndexToWorldTransform)
//## \sa SetIndexToWorldTransform
void GeometryTransformHolder::TransferVtkToItkTransform()
{
TransferVtkMatrixToItkTransform(m_VtkMatrix, m_IndexToWorldTransform.GetPointer());
}
//##Documentation
//## @brief Get the origin, e.g. the upper-left corner of the plane
const Point3D GeometryTransformHolder::GetOrigin() const
{
Point3D origin = Point3D(this->GetIndexToWorldTransform()->GetOffset());
return origin;
}
//##Documentation
//## @brief Set the origin, i.e. the upper-left corner of the plane
//##
void GeometryTransformHolder::SetOrigin(const Point3D &origin)
{
m_IndexToWorldTransform->SetOffset(origin.GetVectorFromOrigin());
TransferItkToVtkTransform();
}
//##Documentation
//## @brief Get the spacing (size of a pixel).
//##
const mitk::Vector3D GeometryTransformHolder::GetSpacing() const
{
mitk::Vector3D spacing;
CopySpacingFromTransform(this->GetIndexToWorldTransform(), spacing);
return spacing;
}
//##Documentation
//## @brief Set the spacing.
//##
//##The spacing is also changed in the IndexToWorldTransform.
void GeometryTransformHolder::SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing)
{
if (mitk::Equal(this->GetSpacing(), aSpacing) == false || enforceSetSpacing)
{
assert(aSpacing[0] > 0 && aSpacing[1] > 0 && aSpacing[2] > 0);
AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix;
vnlmatrix = m_IndexToWorldTransform->GetMatrix().GetVnlMatrix();
mitk::VnlVector col;
- col = vnlmatrix.get_column(0);
+ col = vnlmatrix.get_column(0).as_ref();
col.normalize();
col *= aSpacing[0];
vnlmatrix.set_column(0, col);
- col = vnlmatrix.get_column(1);
+ col = vnlmatrix.get_column(1).as_ref();
col.normalize();
col *= aSpacing[1];
vnlmatrix.set_column(1, col);
- col = vnlmatrix.get_column(2);
+ col = vnlmatrix.get_column(2).as_ref();
col.normalize();
col *= aSpacing[2];
vnlmatrix.set_column(2, col);
Matrix3D matrix;
matrix = vnlmatrix;
AffineTransform3D::Pointer transform = AffineTransform3D::New();
transform->SetMatrix(matrix);
transform->SetOffset(m_IndexToWorldTransform->GetOffset());
SetIndexToWorldTransform(transform.GetPointer());
}
}
//##Documentation
//## @brief Get the transformation used to convert from index
//## to world coordinates
mitk::AffineTransform3D *GeometryTransformHolder::GetIndexToWorldTransform() { return m_IndexToWorldTransform; }
//##Documentation
//## @brief Get the transformation used to convert from index
//## to world coordinates
const mitk::AffineTransform3D *GeometryTransformHolder::GetIndexToWorldTransform() const
{
return m_IndexToWorldTransform;
}
//## @brief Set the transformation used to convert from index
//## to world coordinates. The spacing of the new transform is
//## copied to m_spacing.
void GeometryTransformHolder::SetIndexToWorldTransform(mitk::AffineTransform3D *transform)
{
m_IndexToWorldTransform = transform;
TransferItkToVtkTransform();
}
//##Documentation
//## @brief Convenience method for setting the ITK transform
//## (m_IndexToWorldTransform) via an vtkMatrix4x4.The spacing of
//## the new transform is copied to m_spacing.
//## \sa SetIndexToWorldTransform
void GeometryTransformHolder::SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4 *vtkmatrix)
{
m_VtkMatrix->DeepCopy(vtkmatrix);
TransferVtkToItkTransform();
}
//## @brief Set the transformation used to convert from index
//## to world coordinates.This function keeps the original spacing.
void GeometryTransformHolder::SetIndexToWorldTransformWithoutChangingSpacing(mitk::AffineTransform3D *transform)
{
mitk::Vector3D originalSpacing = this->GetSpacing();
this->SetIndexToWorldTransform(transform);
this->SetSpacing(originalSpacing);
}
//##Documentation
//## @brief Convenience method for setting the ITK transform
//## (m_IndexToWorldTransform) via an vtkMatrix4x4. This function keeps the original spacing.
//## \sa SetIndexToWorldTransform
void GeometryTransformHolder::SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkMatrix4x4 *vtkmatrix)
{
mitk::Vector3D originalSpacing = this->GetSpacing();
this->SetIndexToWorldTransformByVtkMatrix(vtkmatrix);
this->SetSpacing(originalSpacing);
}
//## Get the Vtk Matrix which describes the transform.
vtkMatrix4x4 *GeometryTransformHolder::GetVtkMatrix() { return m_VtkMatrix; }
//## Get the Vtk Matrix which describes the transform.
const vtkMatrix4x4 *GeometryTransformHolder::GetVtkMatrix() const { return m_VtkMatrix; }
//##Documentation
//## @brief Get the m_IndexToWorldTransform as a vtkLinearTransform
vtkLinearTransform *GeometryTransformHolder::GetVtkTransform() const
{
return (vtkLinearTransform *)m_VtkIndexToWorldTransform;
}
void GeometryTransformHolder::SetMatrix(Matrix3D &matrix)
{
m_IndexToWorldTransform->SetMatrix(matrix);
TransferItkToVtkTransform();
}
void GeometryTransformHolder::SetIdentity()
{
m_IndexToWorldTransform->SetIdentity();
TransferItkToVtkTransform();
}
void GeometryTransformHolder::Compose(const TransformType *other, bool pre)
{
m_IndexToWorldTransform->Compose(other, pre);
TransferItkToVtkTransform();
}
void GeometryTransformHolder::SetVtkMatrixDeepCopy(vtkTransform *vtktransform)
{
m_VtkMatrix->DeepCopy(vtktransform->GetMatrix());
TransferVtkToItkTransform();
}
bool GeometryTransformHolder::IsIndexToWorldTransformNull() { return m_IndexToWorldTransform.IsNull(); }
AffineTransform3D::MatrixType::InternalMatrixType GeometryTransformHolder::GetVnlMatrix()
{
return m_IndexToWorldTransform->GetMatrix().GetVnlMatrix();
}
}
bool mitk::Equal(const mitk::GeometryTransformHolder *leftHandSide,
const mitk::GeometryTransformHolder *rightHandSide,
ScalarType eps,
bool verbose)
{
if ((leftHandSide == nullptr) || (rightHandSide == nullptr))
{
MITK_ERROR << "mitk::Equal(const mitk::Geometry3D *leftHandSide, const mitk::Geometry3D *rightHandSide, ScalarType "
"eps, bool verbose) does not with nullptr pointer input.";
return false;
}
return Equal(*leftHandSide, *rightHandSide, eps, verbose);
}
bool mitk::Equal(const mitk::GeometryTransformHolder &leftHandSide,
const mitk::GeometryTransformHolder &rightHandSide,
ScalarType eps,
bool verbose)
{
bool result = true;
// Compare spacings
if (!mitk::Equal(leftHandSide.GetSpacing(), rightHandSide.GetSpacing(), eps))
{
if (verbose)
{
MITK_INFO << "[( Geometry3D )] Spacing differs.";
MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetSpacing() << " : leftHandSide is "
<< leftHandSide.GetSpacing() << " and tolerance is " << eps;
}
result = false;
}
// Compare Origins
if (!mitk::Equal(leftHandSide.GetOrigin(), rightHandSide.GetOrigin(), eps))
{
if (verbose)
{
MITK_INFO << "[( Geometry3D )] Origin differs.";
MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetOrigin() << " : leftHandSide is "
<< leftHandSide.GetOrigin() << " and tolerance is " << eps;
}
result = false;
}
// Compare IndexToWorldTransform Matrix
if (!mitk::Equal(*leftHandSide.GetIndexToWorldTransform(), *rightHandSide.GetIndexToWorldTransform(), eps, verbose))
{
result = false;
}
// Compare vtk Matrix
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (leftHandSide.GetVtkMatrix()->GetElement(i, j) != rightHandSide.GetVtkMatrix()->GetElement(i, j))
{
result = false;
}
}
}
return result;
}
diff --git a/Modules/Core/src/DataManagement/mitkImage.cpp b/Modules/Core/src/DataManagement/mitkImage.cpp
index bd5ba5e95d..0c10c1302a 100644
--- a/Modules/Core/src/DataManagement/mitkImage.cpp
+++ b/Modules/Core/src/DataManagement/mitkImage.cpp
@@ -1,1358 +1,1355 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// MITK
#include "mitkImage.h"
#include "mitkCompareImageDataFilter.h"
#include "mitkImageStatisticsHolder.h"
#include "mitkImageVtkReadAccessor.h"
#include "mitkImageVtkWriteAccessor.h"
#include "mitkPixelTypeMultiplex.h"
#include <mitkProportionalTimeGeometry.h>
// VTK
#include <vtkImageData.h>
-// ITK
-#include <itkMutexLockHolder.h>
-
// Other
#include <cmath>
#define FILL_C_ARRAY(_arr, _size, _value) \
for (unsigned int i = 0u; i < _size; i++) \
\
{ \
_arr[i] = _value; \
}
mitk::Image::Image()
: m_Dimension(0),
m_Dimensions(nullptr),
m_ImageDescriptor(nullptr),
m_OffsetTable(nullptr),
m_CompleteData(nullptr),
m_ImageStatistics(nullptr)
{
m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS];
FILL_C_ARRAY(m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u);
m_Initialized = false;
}
mitk::Image::Image(const Image &other)
: SlicedData(other),
m_Dimension(0),
m_Dimensions(nullptr),
m_ImageDescriptor(nullptr),
m_OffsetTable(nullptr),
m_CompleteData(nullptr),
m_ImageStatistics(nullptr)
{
m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS];
FILL_C_ARRAY(m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u);
this->Initialize(other.GetPixelType(), other.GetDimension(), other.GetDimensions());
// Since the above called "Initialize" method doesn't take the geometry into account we need to set it
// here manually
TimeGeometry::Pointer cloned = other.GetTimeGeometry()->Clone();
this->SetTimeGeometry(cloned.GetPointer());
if (this->GetDimension() > 3)
{
const unsigned int time_steps = this->GetDimension(3);
for (unsigned int i = 0u; i < time_steps; ++i)
{
ImageDataItemPointer volume = other.GetVolumeData(i);
this->SetVolume(volume->GetData(), i);
}
}
else
{
ImageDataItemPointer volume = other.GetVolumeData(0);
this->SetVolume(volume->GetData(), 0);
}
}
mitk::Image::~Image()
{
this->Clear();
m_ReferenceCount = 3;
m_ReferenceCount = 0;
delete[] m_OffsetTable;
delete m_ImageStatistics;
}
const mitk::PixelType mitk::Image::GetPixelType(int n) const
{
return this->m_ImageDescriptor->GetChannelTypeById(n);
}
unsigned int mitk::Image::GetDimension() const
{
return m_Dimension;
}
unsigned int mitk::Image::GetDimension(int i) const
{
if ((i >= 0) && (i < (int)m_Dimension))
return m_Dimensions[i];
return 1;
}
template <class T>
void AccessPixel(const mitk::PixelType ptype, void *data, const unsigned int offset, double &value)
{
value = 0.0;
if (data == nullptr)
return;
if (ptype.GetBpe() != 24)
{
value = (double)(((T *)data)[offset]);
}
else
{
const unsigned int rgboffset = offset;
double returnvalue = (((T *)data)[rgboffset]);
returnvalue += (((T *)data)[rgboffset + 1]);
returnvalue += (((T *)data)[rgboffset + 2]);
value = returnvalue;
}
}
vtkImageData *mitk::Image::GetVtkImageData(int t, int n)
{
if (m_Initialized == false)
{
if (GetSource().IsNull())
return nullptr;
if (GetSource()->Updating() == false)
GetSource()->UpdateOutputInformation();
}
ImageDataItemPointer volume = GetVolumeData(t, n);
return volume.GetPointer() == nullptr ? nullptr : volume->GetVtkImageAccessor(this)->GetVtkImageData();
}
const vtkImageData *mitk::Image::GetVtkImageData(int t, int n) const
{
if (m_Initialized == false)
{
if (GetSource().IsNull())
return nullptr;
if (GetSource()->Updating() == false)
GetSource()->UpdateOutputInformation();
}
ImageDataItemPointer volume = GetVolumeData(t, n);
return volume.GetPointer() == nullptr ? nullptr : volume->GetVtkImageAccessor(this)->GetVtkImageData();
}
mitk::Image::ImageDataItemPointer mitk::Image::GetSliceData(
int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const
{
MutexHolder lock(m_ImageDataArraysLock);
return GetSliceData_unlocked(s, t, n, data, importMemoryManagement);
}
mitk::Image::ImageDataItemPointer mitk::Image::GetSliceData_unlocked(
int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const
{
if (IsValidSlice(s, t, n) == false)
return nullptr;
const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize();
// slice directly available?
int pos = GetSliceIndex(s, t, n);
if (m_Slices[pos].GetPointer() != nullptr)
{
return m_Slices[pos];
}
// is slice available as part of a volume that is available?
ImageDataItemPointer sl, ch, vol;
vol = m_Volumes[GetVolumeIndex(t, n)];
if ((vol.GetPointer() != nullptr) && (vol->IsComplete()))
{
sl = new ImageDataItem(*vol,
m_ImageDescriptor,
t,
2,
data,
importMemoryManagement == ManageMemory,
((size_t)s) * m_OffsetTable[2] * (ptypeSize));
sl->SetComplete(true);
return m_Slices[pos] = sl;
}
// is slice available as part of a channel that is available?
ch = m_Channels[n];
if ((ch.GetPointer() != nullptr) && (ch->IsComplete()))
{
sl = new ImageDataItem(*ch,
m_ImageDescriptor,
t,
2,
data,
importMemoryManagement == ManageMemory,
(((size_t)s) * m_OffsetTable[2] + ((size_t)t) * m_OffsetTable[3]) * (ptypeSize));
sl->SetComplete(true);
return m_Slices[pos] = sl;
}
// slice is unavailable. Can we calculate it?
if ((GetSource().IsNotNull()) && (GetSource()->Updating() == false))
{
// ... wir mussen rechnen!!! ....
m_RequestedRegion.SetIndex(0, 0);
m_RequestedRegion.SetIndex(1, 0);
m_RequestedRegion.SetIndex(2, s);
m_RequestedRegion.SetIndex(3, t);
m_RequestedRegion.SetIndex(4, n);
m_RequestedRegion.SetSize(0, m_Dimensions[0]);
m_RequestedRegion.SetSize(1, m_Dimensions[1]);
m_RequestedRegion.SetSize(2, 1);
m_RequestedRegion.SetSize(3, 1);
m_RequestedRegion.SetSize(4, 1);
m_RequestedRegionInitialized = true;
GetSource()->Update();
if (IsSliceSet_unlocked(s, t, n))
// yes: now we can call ourselves without the risk of a endless loop (see "if" above)
return GetSliceData_unlocked(s, t, n, data, importMemoryManagement);
else
return nullptr;
}
else
{
ImageDataItemPointer item = AllocateSliceData_unlocked(s, t, n, data, importMemoryManagement);
item->SetComplete(true);
return item;
}
}
mitk::Image::ImageDataItemPointer mitk::Image::GetVolumeData(int t,
int n,
void *data,
ImportMemoryManagementType importMemoryManagement) const
{
MutexHolder lock(m_ImageDataArraysLock);
return GetVolumeData_unlocked(t, n, data, importMemoryManagement);
}
mitk::Image::ImageDataItemPointer mitk::Image::GetVolumeData_unlocked(
int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const
{
if (IsValidVolume(t, n) == false)
return nullptr;
ImageDataItemPointer ch, vol;
// volume directly available?
int pos = GetVolumeIndex(t, n);
vol = m_Volumes[pos];
if ((vol.GetPointer() != nullptr) && (vol->IsComplete()))
return vol;
const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize();
// is volume available as part of a channel that is available?
ch = m_Channels[n];
if ((ch.GetPointer() != nullptr) && (ch->IsComplete()))
{
vol = new ImageDataItem(*ch,
m_ImageDescriptor,
t,
3,
data,
importMemoryManagement == ManageMemory,
(((size_t)t) * m_OffsetTable[3]) * (ptypeSize));
vol->SetComplete(true);
return m_Volumes[pos] = vol;
}
// let's see if all slices of the volume are set, so that we can (could) combine them to a volume
bool complete = true;
unsigned int s;
for (s = 0; s < m_Dimensions[2]; ++s)
{
if (m_Slices[GetSliceIndex(s, t, n)].GetPointer() == nullptr)
{
complete = false;
break;
}
}
if (complete)
{
// if there is only single slice we do not need to combine anything
if (m_Dimensions[2] <= 1)
{
ImageDataItemPointer sl;
sl = GetSliceData_unlocked(0, t, n, data, importMemoryManagement);
vol = new ImageDataItem(*sl, m_ImageDescriptor, t, 3, data, importMemoryManagement == ManageMemory);
vol->SetComplete(true);
}
else
{
mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(n);
vol = m_Volumes[pos];
// ok, let's combine the slices!
if (vol.GetPointer() == nullptr)
{
vol = new ImageDataItem(chPixelType, t, 3, m_Dimensions, nullptr, true);
}
vol->SetComplete(true);
size_t size = m_OffsetTable[2] * (ptypeSize);
for (s = 0; s < m_Dimensions[2]; ++s)
{
int posSl;
ImageDataItemPointer sl;
posSl = GetSliceIndex(s, t, n);
sl = m_Slices[posSl];
if (sl->GetParent() != vol)
{
// copy data of slices in volume
size_t offset = ((size_t)s) * size;
std::memcpy(static_cast<char *>(vol->GetData()) + offset, sl->GetData(), size);
// replace old slice with reference to volume
sl = new ImageDataItem(
*vol, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, ((size_t)s) * size);
sl->SetComplete(true);
m_Slices[posSl] = sl;
}
}
}
return m_Volumes[pos] = vol;
}
// volume is unavailable. Can we calculate it?
if ((GetSource().IsNotNull()) && (GetSource()->Updating() == false))
{
// ... wir muessen rechnen!!! ....
m_RequestedRegion.SetIndex(0, 0);
m_RequestedRegion.SetIndex(1, 0);
m_RequestedRegion.SetIndex(2, 0);
m_RequestedRegion.SetIndex(3, t);
m_RequestedRegion.SetIndex(4, n);
m_RequestedRegion.SetSize(0, m_Dimensions[0]);
m_RequestedRegion.SetSize(1, m_Dimensions[1]);
m_RequestedRegion.SetSize(2, m_Dimensions[2]);
m_RequestedRegion.SetSize(3, 1);
m_RequestedRegion.SetSize(4, 1);
m_RequestedRegionInitialized = true;
GetSource()->Update();
if (IsVolumeSet_unlocked(t, n))
// yes: now we can call ourselves without the risk of a endless loop (see "if" above)
return GetVolumeData_unlocked(t, n, data, importMemoryManagement);
else
return nullptr;
}
else
{
ImageDataItemPointer item = AllocateVolumeData_unlocked(t, n, data, importMemoryManagement);
item->SetComplete(true);
return item;
}
}
mitk::Image::ImageDataItemPointer mitk::Image::GetChannelData(int n,
void *data,
ImportMemoryManagementType importMemoryManagement) const
{
MutexHolder lock(m_ImageDataArraysLock);
return GetChannelData_unlocked(n, data, importMemoryManagement);
}
mitk::Image::ImageDataItemPointer mitk::Image::GetChannelData_unlocked(
int n, void *data, ImportMemoryManagementType importMemoryManagement) const
{
if (IsValidChannel(n) == false)
return nullptr;
ImageDataItemPointer ch, vol;
ch = m_Channels[n];
if ((ch.GetPointer() != nullptr) && (ch->IsComplete()))
return ch;
// let's see if all volumes are set, so that we can (could) combine them to a channel
if (IsChannelSet_unlocked(n))
{
// if there is only one time frame we do not need to combine anything
if (m_Dimensions[3] <= 1)
{
vol = GetVolumeData_unlocked(0, n, data, importMemoryManagement);
ch = new ImageDataItem(*vol,
m_ImageDescriptor,
0,
m_ImageDescriptor->GetNumberOfDimensions(),
data,
importMemoryManagement == ManageMemory);
ch->SetComplete(true);
}
else
{
const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize();
ch = m_Channels[n];
// ok, let's combine the volumes!
if (ch.GetPointer() == nullptr)
ch = new ImageDataItem(this->m_ImageDescriptor, -1, nullptr, true);
ch->SetComplete(true);
size_t size = m_OffsetTable[m_Dimension - 1] * (ptypeSize);
unsigned int t;
auto slicesIt = m_Slices.begin() + n * m_Dimensions[2] * m_Dimensions[3];
for (t = 0; t < m_Dimensions[3]; ++t)
{
int posVol;
ImageDataItemPointer vol;
posVol = GetVolumeIndex(t, n);
vol = GetVolumeData_unlocked(t, n, data, importMemoryManagement);
if (vol->GetParent() != ch)
{
// copy data of volume in channel
size_t offset = ((size_t)t) * m_OffsetTable[3] * (ptypeSize);
std::memcpy(static_cast<char *>(ch->GetData()) + offset, vol->GetData(), size);
// replace old volume with reference to channel
vol = new ImageDataItem(*ch, m_ImageDescriptor, t, 3, data, importMemoryManagement == ManageMemory, offset);
vol->SetComplete(true);
m_Volumes[posVol] = vol;
// get rid of slices - they may point to old volume
ImageDataItemPointer dnull = nullptr;
for (unsigned int i = 0; i < m_Dimensions[2]; ++i, ++slicesIt)
{
assert(slicesIt != m_Slices.end());
*slicesIt = dnull;
}
}
}
}
return m_Channels[n] = ch;
}
// channel is unavailable. Can we calculate it?
if ((GetSource().IsNotNull()) && (GetSource()->Updating() == false))
{
// ... wir muessen rechnen!!! ....
m_RequestedRegion.SetIndex(0, 0);
m_RequestedRegion.SetIndex(1, 0);
m_RequestedRegion.SetIndex(2, 0);
m_RequestedRegion.SetIndex(3, 0);
m_RequestedRegion.SetIndex(4, n);
m_RequestedRegion.SetSize(0, m_Dimensions[0]);
m_RequestedRegion.SetSize(1, m_Dimensions[1]);
m_RequestedRegion.SetSize(2, m_Dimensions[2]);
m_RequestedRegion.SetSize(3, m_Dimensions[3]);
m_RequestedRegion.SetSize(4, 1);
m_RequestedRegionInitialized = true;
GetSource()->Update();
// did it work?
if (IsChannelSet_unlocked(n))
// yes: now we can call ourselves without the risk of a endless loop (see "if" above)
return GetChannelData_unlocked(n, data, importMemoryManagement);
else
return nullptr;
}
else
{
ImageDataItemPointer item = AllocateChannelData_unlocked(n, data, importMemoryManagement);
item->SetComplete(true);
return item;
}
}
bool mitk::Image::IsSliceSet(int s, int t, int n) const
{
MutexHolder lock(m_ImageDataArraysLock);
return IsSliceSet_unlocked(s, t, n);
}
bool mitk::Image::IsSliceSet_unlocked(int s, int t, int n) const
{
if (IsValidSlice(s, t, n) == false)
return false;
if (m_Slices[GetSliceIndex(s, t, n)].GetPointer() != nullptr)
{
return true;
}
ImageDataItemPointer ch, vol;
vol = m_Volumes[GetVolumeIndex(t, n)];
if ((vol.GetPointer() != nullptr) && (vol->IsComplete()))
{
return true;
}
ch = m_Channels[n];
if ((ch.GetPointer() != nullptr) && (ch->IsComplete()))
{
return true;
}
return false;
}
bool mitk::Image::IsVolumeSet(int t, int n) const
{
MutexHolder lock(m_ImageDataArraysLock);
return IsVolumeSet_unlocked(t, n);
}
bool mitk::Image::IsVolumeSet_unlocked(int t, int n) const
{
if (IsValidVolume(t, n) == false)
return false;
ImageDataItemPointer ch, vol;
// volume directly available?
vol = m_Volumes[GetVolumeIndex(t, n)];
if ((vol.GetPointer() != nullptr) && (vol->IsComplete()))
return true;
// is volume available as part of a channel that is available?
ch = m_Channels[n];
if ((ch.GetPointer() != nullptr) && (ch->IsComplete()))
return true;
// let's see if all slices of the volume are set, so that we can (could) combine them to a volume
unsigned int s;
for (s = 0; s < m_Dimensions[2]; ++s)
{
if (m_Slices[GetSliceIndex(s, t, n)].GetPointer() == nullptr)
{
return false;
}
}
return true;
}
bool mitk::Image::IsChannelSet(int n) const
{
MutexHolder lock(m_ImageDataArraysLock);
return IsChannelSet_unlocked(n);
}
bool mitk::Image::IsChannelSet_unlocked(int n) const
{
if (IsValidChannel(n) == false)
return false;
ImageDataItemPointer ch, vol;
ch = m_Channels[n];
if ((ch.GetPointer() != nullptr) && (ch->IsComplete()))
return true;
// let's see if all volumes are set, so that we can (could) combine them to a channel
unsigned int t;
for (t = 0; t < m_Dimensions[3]; ++t)
{
if (IsVolumeSet_unlocked(t, n) == false)
{
return false;
}
}
return true;
}
bool mitk::Image::SetSlice(const void *data, int s, int t, int n)
{
// const_cast is no risk for ImportMemoryManagementType == CopyMemory
return SetImportSlice(const_cast<void *>(data), s, t, n, CopyMemory);
}
bool mitk::Image::SetVolume(const void *data, int t, int n)
{
// const_cast is no risk for ImportMemoryManagementType == CopyMemory
return SetImportVolume(const_cast<void *>(data), t, n, CopyMemory);
}
bool mitk::Image::SetChannel(const void *data, int n)
{
// const_cast is no risk for ImportMemoryManagementType == CopyMemory
return SetImportChannel(const_cast<void *>(data), n, CopyMemory);
}
bool mitk::Image::SetImportSlice(void *data, int s, int t, int n, ImportMemoryManagementType importMemoryManagement)
{
if (IsValidSlice(s, t, n) == false)
return false;
ImageDataItemPointer sl;
const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize();
if (IsSliceSet(s, t, n))
{
sl = GetSliceData(s, t, n, data, importMemoryManagement);
if (sl->GetManageMemory() == false)
{
sl = AllocateSliceData(s, t, n, data, importMemoryManagement);
if (sl.GetPointer() == nullptr)
return false;
}
if (sl->GetData() != data)
std::memcpy(sl->GetData(), data, m_OffsetTable[2] * (ptypeSize));
sl->Modified();
// we have changed the data: call Modified()!
Modified();
}
else
{
sl = AllocateSliceData(s, t, n, data, importMemoryManagement);
if (sl.GetPointer() == nullptr)
return false;
if (sl->GetData() != data)
std::memcpy(sl->GetData(), data, m_OffsetTable[2] * (ptypeSize));
// we just added a missing slice, which is not regarded as modification.
// Therefore, we do not call Modified()!
}
return true;
}
bool mitk::Image::SetImportVolume(void *data, int t, int n, ImportMemoryManagementType importMemoryManagement)
{
if (IsValidVolume(t, n) == false)
return false;
const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize();
ImageDataItemPointer vol;
if (IsVolumeSet(t, n))
{
vol = GetVolumeData(t, n, data, importMemoryManagement);
if (vol->GetManageMemory() == false)
{
vol = AllocateVolumeData(t, n, data, importMemoryManagement);
if (vol.GetPointer() == nullptr)
return false;
}
if (vol->GetData() != data)
std::memcpy(vol->GetData(), data, m_OffsetTable[3] * (ptypeSize));
vol->Modified();
vol->SetComplete(true);
// we have changed the data: call Modified()!
Modified();
}
else
{
vol = AllocateVolumeData(t, n, data, importMemoryManagement);
if (vol.GetPointer() == nullptr)
return false;
if (vol->GetData() != data)
{
std::memcpy(vol->GetData(), data, m_OffsetTable[3] * (ptypeSize));
}
vol->SetComplete(true);
this->m_ImageDescriptor->GetChannelDescriptor(n).SetData(vol->GetData());
// we just added a missing Volume, which is not regarded as modification.
// Therefore, we do not call Modified()!
}
return true;
}
bool mitk::Image::SetImportVolume(const void *const_data, int t, int n)
{
return this->SetImportVolume(const_cast<void*>(const_data), t, n, CopyMemory);
}
bool mitk::Image::SetImportChannel(void *data, int n, ImportMemoryManagementType importMemoryManagement)
{
if (IsValidChannel(n) == false)
return false;
// channel descriptor
const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize();
ImageDataItemPointer ch;
if (IsChannelSet(n))
{
ch = GetChannelData(n, data, importMemoryManagement);
if (ch->GetManageMemory() == false)
{
ch = AllocateChannelData(n, data, importMemoryManagement);
if (ch.GetPointer() == nullptr)
return false;
}
if (ch->GetData() != data)
std::memcpy(ch->GetData(), data, m_OffsetTable[4] * (ptypeSize));
ch->Modified();
ch->SetComplete(true);
// we have changed the data: call Modified()!
Modified();
}
else
{
ch = AllocateChannelData(n, data, importMemoryManagement);
if (ch.GetPointer() == nullptr)
return false;
if (ch->GetData() != data)
std::memcpy(ch->GetData(), data, m_OffsetTable[4] * (ptypeSize));
ch->SetComplete(true);
this->m_ImageDescriptor->GetChannelDescriptor(n).SetData(ch->GetData());
// we just added a missing Channel, which is not regarded as modification.
// Therefore, we do not call Modified()!
}
return true;
}
void mitk::Image::Initialize()
{
ImageDataItemPointerArray::iterator it, end;
for (it = m_Slices.begin(), end = m_Slices.end(); it != end; ++it)
{
(*it) = nullptr;
}
for (it = m_Volumes.begin(), end = m_Volumes.end(); it != end; ++it)
{
(*it) = nullptr;
}
for (it = m_Channels.begin(), end = m_Channels.end(); it != end; ++it)
{
(*it) = nullptr;
}
m_CompleteData = nullptr;
if (m_ImageStatistics == nullptr)
{
m_ImageStatistics = new mitk::ImageStatisticsHolder(this);
}
SetRequestedRegionToLargestPossibleRegion();
}
void mitk::Image::Initialize(const mitk::ImageDescriptor::Pointer inDesc)
{
// store the descriptor
this->m_ImageDescriptor = inDesc;
// initialize image
this->Initialize(
inDesc->GetChannelDescriptor(0).GetPixelType(), inDesc->GetNumberOfDimensions(), inDesc->GetDimensions(), 1);
}
void mitk::Image::Initialize(const mitk::PixelType &type,
unsigned int dimension,
const unsigned int *dimensions,
unsigned int channels)
{
Clear();
m_Dimension = dimension;
if (!dimensions)
itkExceptionMacro(<< "invalid zero dimension image");
unsigned int i;
for (i = 0; i < dimension; ++i)
{
if (dimensions[i] < 1)
itkExceptionMacro(<< "invalid dimension[" << i << "]: " << dimensions[i]);
}
// create new array since the old was deleted
m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS];
// initialize the first four dimensions to 1, the remaining 4 to 0
FILL_C_ARRAY(m_Dimensions, 4, 1u);
FILL_C_ARRAY((m_Dimensions + 4), 4, 0u);
// copy in the passed dimension information
std::memcpy(m_Dimensions, dimensions, sizeof(unsigned int) * m_Dimension);
this->m_ImageDescriptor = mitk::ImageDescriptor::New();
this->m_ImageDescriptor->Initialize(this->m_Dimensions, this->m_Dimension);
for (i = 0; i < 4; ++i)
{
m_LargestPossibleRegion.SetIndex(i, 0);
m_LargestPossibleRegion.SetSize(i, m_Dimensions[i]);
}
m_LargestPossibleRegion.SetIndex(i, 0);
m_LargestPossibleRegion.SetSize(i, channels);
if (m_LargestPossibleRegion.GetNumberOfPixels() == 0)
{
delete[] m_Dimensions;
m_Dimensions = nullptr;
return;
}
for (unsigned int i = 0u; i < channels; i++)
{
this->m_ImageDescriptor->AddNewChannel(type);
}
PlaneGeometry::Pointer planegeometry = PlaneGeometry::New();
planegeometry->InitializeStandardPlane(m_Dimensions[0], m_Dimensions[1]);
SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New();
slicedGeometry->InitializeEvenlySpaced(planegeometry, m_Dimensions[2]);
ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New();
timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]);
for (TimeStepType step = 0; step < timeGeometry->CountTimeSteps(); ++step)
{
timeGeometry->GetGeometryForTimeStep(step)->ImageGeometryOn();
}
SetTimeGeometry(timeGeometry);
ImageDataItemPointer dnull = nullptr;
m_Channels.assign(GetNumberOfChannels(), dnull);
m_Volumes.assign(GetNumberOfChannels() * m_Dimensions[3], dnull);
m_Slices.assign(GetNumberOfChannels() * m_Dimensions[3] * m_Dimensions[2], dnull);
ComputeOffsetTable();
Initialize();
m_Initialized = true;
}
void mitk::Image::Initialize(const mitk::PixelType &type,
const mitk::BaseGeometry &geometry,
unsigned int channels,
int tDim)
{
mitk::ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New();
timeGeometry->Initialize(geometry.Clone(), tDim);
this->Initialize(type, *timeGeometry, channels, tDim);
}
void mitk::Image::Initialize(const mitk::PixelType &type,
const mitk::TimeGeometry &geometry,
unsigned int channels,
int tDim)
{
unsigned int dimensions[5];
dimensions[0] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(0) + 0.5);
dimensions[1] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(1) + 0.5);
dimensions[2] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(2) + 0.5);
dimensions[3] = (tDim > 0) ? tDim : geometry.CountTimeSteps();
dimensions[4] = 0;
unsigned int dimension = 2;
if (dimensions[2] > 1)
dimension = 3;
if (dimensions[3] > 1)
dimension = 4;
Initialize(type, dimension, dimensions, channels);
if (geometry.CountTimeSteps() > 1)
{
TimeGeometry::Pointer cloned = geometry.Clone();
SetTimeGeometry(cloned.GetPointer());
// make sure the image geometry flag is properly set for all time steps
for (TimeStepType step = 0; step < cloned->CountTimeSteps(); ++step)
{
if (!cloned->GetGeometryCloneForTimeStep(step)->GetImageGeometry())
{
MITK_WARN("Image.3DnT.Initialize") << " Attempt to initialize an image with a non-image geometry. "
"Re-interpretting the initialization geometry for timestep "
<< step << " as image geometry, the original geometry remains unchanged.";
cloned->GetGeometryForTimeStep(step)->ImageGeometryOn();
}
}
}
else
{
// make sure the image geometry coming from outside has proper value of the image geometry flag
BaseGeometry::Pointer cloned = geometry.GetGeometryCloneForTimeStep(0)->Clone();
if (!cloned->GetImageGeometry())
{
MITK_WARN("Image.Initialize") << " Attempt to initialize an image with a non-image geometry. Re-interpretting "
"the initialization geometry as image geometry, the original geometry remains "
"unchanged.";
cloned->ImageGeometryOn();
}
Superclass::SetGeometry(cloned);
}
}
void mitk::Image::Initialize(const mitk::PixelType &type,
int sDim,
const mitk::PlaneGeometry &geometry2d,
unsigned int channels,
int tDim)
{
SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New();
slicedGeometry->InitializeEvenlySpaced(geometry2d.Clone(), sDim);
Initialize(type, *slicedGeometry, channels, tDim);
}
void mitk::Image::Initialize(const mitk::Image *image)
{
Initialize(image->GetPixelType(), *image->GetTimeGeometry());
}
void mitk::Image::Initialize(vtkImageData *vtkimagedata, int channels, int tDim, int sDim, int pDim)
{
if (vtkimagedata == nullptr)
return;
m_Dimension = vtkimagedata->GetDataDimension();
unsigned int i, *tmpDimensions = new unsigned int[m_Dimension > 4 ? m_Dimension : 4];
for (i = 0; i < m_Dimension; ++i)
tmpDimensions[i] = vtkimagedata->GetDimensions()[i];
if (m_Dimension < 4)
{
unsigned int *p;
for (i = 0, p = tmpDimensions + m_Dimension; i < 4 - m_Dimension; ++i, ++p)
*p = 1;
}
if (pDim >= 0)
{
tmpDimensions[1] = pDim;
if (m_Dimension < 2)
m_Dimension = 2;
}
if (sDim >= 0)
{
tmpDimensions[2] = sDim;
if (m_Dimension < 3)
m_Dimension = 3;
}
if (tDim >= 0)
{
tmpDimensions[3] = tDim;
if (m_Dimension < 4)
m_Dimension = 4;
}
mitk::PixelType pixelType(MakePixelType(vtkimagedata));
Initialize(pixelType, m_Dimension, tmpDimensions, channels);
const double *spacinglist = vtkimagedata->GetSpacing();
Vector3D spacing;
FillVector3D(spacing, spacinglist[0], 1.0, 1.0);
if (m_Dimension >= 2)
spacing[1] = spacinglist[1];
if (m_Dimension >= 3)
spacing[2] = spacinglist[2];
// access origin of vtkImage
Point3D origin;
double vtkorigin[3];
vtkimagedata->GetOrigin(vtkorigin);
FillVector3D(origin, vtkorigin[0], 0.0, 0.0);
if (m_Dimension >= 2)
origin[1] = vtkorigin[1];
if (m_Dimension >= 3)
origin[2] = vtkorigin[2];
SlicedGeometry3D *slicedGeometry = GetSlicedGeometry(0);
// re-initialize PlaneGeometry with origin and direction
auto *planeGeometry = static_cast<PlaneGeometry *>(slicedGeometry->GetPlaneGeometry(0));
planeGeometry->SetOrigin(origin);
// re-initialize SlicedGeometry3D
slicedGeometry->SetOrigin(origin);
slicedGeometry->SetSpacing(spacing);
ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New();
timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]);
SetTimeGeometry(timeGeometry);
delete[] tmpDimensions;
}
bool mitk::Image::IsValidSlice(int s, int t, int n) const
{
if (m_Initialized)
return ((s >= 0) && (s < (int)m_Dimensions[2]) && (t >= 0) && (t < (int)m_Dimensions[3]) && (n >= 0) &&
(n < (int)GetNumberOfChannels()));
else
return false;
}
bool mitk::Image::IsValidVolume(int t, int n) const
{
if (m_Initialized)
return IsValidSlice(0, t, n);
else
return false;
}
bool mitk::Image::IsValidChannel(int n) const
{
if (m_Initialized)
return IsValidSlice(0, 0, n);
else
return false;
}
void mitk::Image::ComputeOffsetTable()
{
if (m_OffsetTable != nullptr)
delete[] m_OffsetTable;
m_OffsetTable = new size_t[m_Dimension > 4 ? m_Dimension + 1 : 4 + 1];
unsigned int i;
size_t num = 1;
m_OffsetTable[0] = 1;
for (i = 0; i < m_Dimension; ++i)
{
num *= m_Dimensions[i];
m_OffsetTable[i + 1] = num;
}
for (; i < 4; ++i)
m_OffsetTable[i + 1] = num;
}
bool mitk::Image::IsValidTimeStep(int t) const
{
return ((m_Dimension >= 4 && t <= (int)m_Dimensions[3] && t > 0) || (t == 0));
}
void mitk::Image::Expand(unsigned int timeSteps)
{
if (timeSteps < 1)
itkExceptionMacro(<< "Invalid timestep in Image!");
Superclass::Expand(timeSteps);
}
int mitk::Image::GetSliceIndex(int s, int t, int n) const
{
if (IsValidSlice(s, t, n) == false)
return false;
return ((size_t)s) + ((size_t)t) * m_Dimensions[2] + ((size_t)n) * m_Dimensions[3] * m_Dimensions[2]; //??
}
int mitk::Image::GetVolumeIndex(int t, int n) const
{
if (IsValidVolume(t, n) == false)
return false;
return ((size_t)t) + ((size_t)n) * m_Dimensions[3]; //??
}
mitk::Image::ImageDataItemPointer mitk::Image::AllocateSliceData(
int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const
{
MutexHolder lock(m_ImageDataArraysLock);
return AllocateSliceData_unlocked(s, t, n, data, importMemoryManagement);
}
mitk::Image::ImageDataItemPointer mitk::Image::AllocateSliceData_unlocked(
int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const
{
int pos;
pos = GetSliceIndex(s, t, n);
const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize();
// is slice available as part of a volume that is available?
ImageDataItemPointer sl, ch, vol;
vol = m_Volumes[GetVolumeIndex(t, n)];
if (vol.GetPointer() != nullptr)
{
sl = new ImageDataItem(*vol,
m_ImageDescriptor,
t,
2,
data,
importMemoryManagement == ManageMemory,
((size_t)s) * m_OffsetTable[2] * (ptypeSize));
sl->SetComplete(true);
return m_Slices[pos] = sl;
}
// is slice available as part of a channel that is available?
ch = m_Channels[n];
if (ch.GetPointer() != nullptr)
{
sl = new ImageDataItem(*ch,
m_ImageDescriptor,
t,
2,
data,
importMemoryManagement == ManageMemory,
(((size_t)s) * m_OffsetTable[2] + ((size_t)t) * m_OffsetTable[3]) * (ptypeSize));
sl->SetComplete(true);
return m_Slices[pos] = sl;
}
// allocate new volume (instead of a single slice to keep data together!)
m_Volumes[GetVolumeIndex(t, n)] = vol = AllocateVolumeData_unlocked(t, n, nullptr, importMemoryManagement);
sl = new ImageDataItem(*vol,
m_ImageDescriptor,
t,
2,
data,
importMemoryManagement == ManageMemory,
((size_t)s) * m_OffsetTable[2] * (ptypeSize));
sl->SetComplete(true);
return m_Slices[pos] = sl;
////ALTERNATIVE:
//// allocate new slice
// sl=new ImageDataItem(*m_PixelType, 2, m_Dimensions);
// m_Slices[pos]=sl;
// return vol;
}
mitk::Image::ImageDataItemPointer mitk::Image::AllocateVolumeData(
int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const
{
MutexHolder lock(m_ImageDataArraysLock);
return AllocateVolumeData_unlocked(t, n, data, importMemoryManagement);
}
mitk::Image::ImageDataItemPointer mitk::Image::AllocateVolumeData_unlocked(
int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const
{
int pos;
pos = GetVolumeIndex(t, n);
const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize();
// is volume available as part of a channel that is available?
ImageDataItemPointer ch, vol;
ch = m_Channels[n];
if (ch.GetPointer() != nullptr)
{
vol = new ImageDataItem(*ch,
m_ImageDescriptor,
t,
3,
data,
importMemoryManagement == ManageMemory,
(((size_t)t) * m_OffsetTable[3]) * (ptypeSize));
return m_Volumes[pos] = vol;
}
mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(n);
// allocate new volume
if (importMemoryManagement == CopyMemory)
{
vol = new ImageDataItem(chPixelType, t, 3, m_Dimensions, nullptr, true);
if (data != nullptr)
std::memcpy(vol->GetData(), data, m_OffsetTable[3] * (ptypeSize));
}
else
{
vol = new ImageDataItem(chPixelType, t, 3, m_Dimensions, data, importMemoryManagement == ManageMemory);
}
m_Volumes[pos] = vol;
return vol;
}
mitk::Image::ImageDataItemPointer mitk::Image::AllocateChannelData(
int n, void *data, ImportMemoryManagementType importMemoryManagement) const
{
MutexHolder lock(m_ImageDataArraysLock);
return AllocateChannelData_unlocked(n, data, importMemoryManagement);
}
mitk::Image::ImageDataItemPointer mitk::Image::AllocateChannelData_unlocked(
int n, void *data, ImportMemoryManagementType importMemoryManagement) const
{
ImageDataItemPointer ch;
// allocate new channel
if (importMemoryManagement == CopyMemory)
{
const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize();
ch = new ImageDataItem(this->m_ImageDescriptor, -1, nullptr, true);
if (data != nullptr)
std::memcpy(ch->GetData(), data, m_OffsetTable[4] * (ptypeSize));
}
else
{
ch = new ImageDataItem(this->m_ImageDescriptor, -1, data, importMemoryManagement == ManageMemory);
}
m_Channels[n] = ch;
return ch;
}
unsigned int *mitk::Image::GetDimensions() const
{
return m_Dimensions;
}
void mitk::Image::Clear()
{
Superclass::Clear();
delete[] m_Dimensions;
m_Dimensions = nullptr;
}
void mitk::Image::SetGeometry(BaseGeometry *aGeometry3D)
{
// Please be aware of the 0.5 offset/pixel-center issue! See Geometry documentation for further information
if (aGeometry3D->GetImageGeometry() == false)
{
MITK_INFO << "WARNING: Applied a non-image geometry onto an image. Please be SURE that this geometry is "
"pixel-center-based! If it is not, you need to call "
"Geometry3D->ChangeImageGeometryConsideringOriginOffset(true) before calling image->setGeometry(..)\n";
}
Superclass::SetGeometry(aGeometry3D);
for (TimeStepType step = 0; step < GetTimeGeometry()->CountTimeSteps(); ++step)
GetTimeGeometry()->GetGeometryForTimeStep(step)->ImageGeometryOn();
}
void mitk::Image::PrintSelf(std::ostream &os, itk::Indent indent) const
{
if (m_Initialized)
{
unsigned char i;
os << indent << " Dimension: " << m_Dimension << std::endl;
os << indent << " Dimensions: ";
for (i = 0; i < m_Dimension; ++i)
os << GetDimension(i) << " ";
os << std::endl;
for (unsigned int ch = 0; ch < this->m_ImageDescriptor->GetNumberOfChannels(); ch++)
{
mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(ch);
os << indent << " Channel: " << this->m_ImageDescriptor->GetChannelName(ch) << std::endl;
os << indent << " PixelType: " << chPixelType.GetPixelTypeAsString() << std::endl;
os << indent << " BytesPerElement: " << chPixelType.GetSize() << std::endl;
os << indent << " ComponentType: " << chPixelType.GetComponentTypeAsString() << std::endl;
os << indent << " NumberOfComponents: " << chPixelType.GetNumberOfComponents() << std::endl;
os << indent << " BitsPerComponent: " << chPixelType.GetBitsPerComponent() << std::endl;
}
}
else
{
os << indent << " Image not initialized: m_Initialized: false" << std::endl;
}
Superclass::PrintSelf(os, indent);
}
bool mitk::Image::IsRotated() const
{
const mitk::BaseGeometry *geo = this->GetGeometry();
bool ret = false;
if (geo)
{
const vnl_matrix_fixed<ScalarType, 3, 3> &mx = geo->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix();
mitk::ScalarType ref = 0;
for (short k = 0; k < 3; ++k)
ref += mx[k][k];
ref /= 1000; // Arbitrary value; if a non-diagonal (nd) element is bigger then this, matrix is considered nd.
for (short i = 0; i < 3; ++i)
{
for (short j = 0; j < 3; ++j)
{
if (i != j)
{
if (std::abs(mx[i][j]) > ref) // matrix is nd
ret = true;
}
}
}
}
return ret;
}
bool mitk::Equal(const mitk::Image &leftHandSide, const mitk::Image &rightHandSide, ScalarType eps, bool verbose)
{
bool returnValue = true;
// Dimensionality
if (rightHandSide.GetDimension() != leftHandSide.GetDimension())
{
if (verbose)
{
MITK_INFO << "[( Image )] Dimensionality differs.";
MITK_INFO << "leftHandSide is " << leftHandSide.GetDimension() << "rightHandSide is "
<< rightHandSide.GetDimension();
}
returnValue = false;
}
// Pair-wise dimension (size) comparison
unsigned int minDimensionality = std::min(rightHandSide.GetDimension(), leftHandSide.GetDimension());
for (unsigned int i = 0; i < minDimensionality; ++i)
{
if (rightHandSide.GetDimension(i) != leftHandSide.GetDimension(i))
{
returnValue = false;
if (verbose)
{
MITK_INFO << "[( Image )] dimension differs.";
MITK_INFO << "leftHandSide->GetDimension(" << i << ") is " << leftHandSide.GetDimension(i)
<< "rightHandSide->GetDimension(" << i << ") is " << rightHandSide.GetDimension(i);
}
}
}
// Pixeltype
mitk::PixelType pixelTypeRightHandSide = rightHandSide.GetPixelType();
mitk::PixelType pixelTypeLeftHandSide = leftHandSide.GetPixelType();
if (!(pixelTypeRightHandSide == pixelTypeLeftHandSide))
{
if (verbose)
{
MITK_INFO << "[( Image )] PixelType differs.";
MITK_INFO << "leftHandSide is " << pixelTypeLeftHandSide.GetTypeAsString() << "rightHandSide is "
<< pixelTypeRightHandSide.GetTypeAsString();
}
returnValue = false;
}
// Geometries
if (!mitk::Equal(*leftHandSide.GetGeometry(), *rightHandSide.GetGeometry(), eps, verbose))
{
if (verbose)
{
MITK_INFO << "[( Image )] Geometries differ.";
}
returnValue = false;
}
// Pixel values - default mode [ 0 threshold in difference ]
// compare only if all previous checks were successfull, otherwise the ITK filter will throw an exception
if (returnValue)
{
mitk::CompareImageDataFilter::Pointer compareFilter = mitk::CompareImageDataFilter::New();
compareFilter->SetInput(0, &rightHandSide);
compareFilter->SetInput(1, &leftHandSide);
compareFilter->SetTolerance(eps);
compareFilter->Update();
if ((!compareFilter->GetResult()))
{
returnValue = false;
if (verbose)
{
MITK_INFO << "[(Image)] Pixel values differ: ";
compareFilter->GetCompareResults().PrintSelf();
}
}
}
return returnValue;
}
diff --git a/Modules/Core/src/DataManagement/mitkImageAccessorBase.cpp b/Modules/Core/src/DataManagement/mitkImageAccessorBase.cpp
index 77e58bf44c..239f195d07 100644
--- a/Modules/Core/src/DataManagement/mitkImageAccessorBase.cpp
+++ b/Modules/Core/src/DataManagement/mitkImageAccessorBase.cpp
@@ -1,184 +1,184 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkImageAccessorBase.h"
#include "mitkImage.h"
mitk::ImageAccessorBase::ThreadIDType mitk::ImageAccessorBase::CurrentThreadHandle()
{
#ifdef ITK_USE_SPROC
return GetCurrentThreadId();
#endif
#ifdef ITK_USE_PTHREADS
return pthread_self();
#endif
#ifdef ITK_USE_WIN32_THREADS
return GetCurrentThreadId();
#endif
}
bool mitk::ImageAccessorBase::CompareThreadHandles(mitk::ImageAccessorBase::ThreadIDType handle1,
mitk::ImageAccessorBase::ThreadIDType handle2)
{
return handle1 == handle2;
}
mitk::ImageAccessorBase::~ImageAccessorBase()
{
}
mitk::ImageAccessorBase::ImageAccessorBase(ImageConstPointer image,
const ImageDataItem *imageDataItem,
int OptionFlags)
: // m_Image(iP)
//, imageDataItem(iDI)
m_SubRegion(nullptr),
m_Options(OptionFlags),
m_CoherentMemory(false)
{
m_Thread = CurrentThreadHandle();
// Initialize WaitLock
m_WaitLock = new ImageAccessorWaitLock();
m_WaitLock->m_WaiterCount = 0;
// Check validity of ImageAccessor
// Is there an Image?
/*
if(!m_Image)
{
mitkThrow() << "Invalid ImageAccessor: No Image was specified in constructor of ImageAccessor";
}
*/
if (image)
{
// Make sure, that the Image is initialized properly
if (image->m_Initialized == false)
{
if (image->GetSource().IsNull())
{
mitkThrow() << "ImageAccessor: No image source is defined";
}
- image->m_ReadWriteLock.Lock();
+ image->m_ReadWriteLock.lock();
if (image->GetSource()->Updating() == false)
{
image->GetSource()->UpdateOutputInformation();
}
- image->m_ReadWriteLock.Unlock();
+ image->m_ReadWriteLock.unlock();
}
}
// Investigate 4 cases of possible image parts/regions
// Case 1: No ImageDataItem and no Subregion => Whole Image is accessed
if (imageDataItem == nullptr && m_SubRegion == nullptr)
{
m_CoherentMemory = true;
// Organize first image channel
- image->m_ReadWriteLock.Lock();
+ image->m_ReadWriteLock.lock();
imageDataItem = image->GetChannelData();
- image->m_ReadWriteLock.Unlock();
+ image->m_ReadWriteLock.unlock();
// Set memory area
m_AddressBegin = imageDataItem->m_Data;
m_AddressEnd = (unsigned char *)m_AddressBegin + imageDataItem->m_Size;
}
// Case 2: ImageDataItem but no Subregion
if (imageDataItem && m_SubRegion == nullptr)
{
m_CoherentMemory = true;
// Set memory area
m_AddressBegin = imageDataItem->m_Data;
m_AddressEnd = (unsigned char *)m_AddressBegin + imageDataItem->m_Size;
}
// Case 3: No ImageDataItem but a SubRegion
if (imageDataItem == nullptr && m_SubRegion)
{
mitkThrow() << "Invalid ImageAccessor: The use of a SubRegion is not supported (yet).";
}
// Case 4: ImageDataItem and SubRegion
if (imageDataItem == nullptr && m_SubRegion)
{
mitkThrow() << "Invalid ImageAccessor: The use of a SubRegion is not supported (yet).";
}
}
/** \brief Computes if there is an Overlap of the image part between this instantiation and another ImageAccessor object
* \throws mitk::Exception if memory area is incoherent (not supported yet)
*/
bool mitk::ImageAccessorBase::Overlap(const ImageAccessorBase *iAB)
{
if (m_CoherentMemory)
{
if ((iAB->m_AddressBegin >= m_AddressBegin && iAB->m_AddressBegin < m_AddressEnd) ||
(iAB->m_AddressEnd > m_AddressBegin && iAB->m_AddressEnd <= m_AddressEnd))
{
return true;
}
if ((m_AddressBegin >= iAB->m_AddressBegin && m_AddressBegin < iAB->m_AddressEnd) ||
(m_AddressEnd > iAB->m_AddressBegin && m_AddressEnd <= iAB->m_AddressEnd))
{
return true;
}
}
else
{
- GetImage()->m_ReadWriteLock.Unlock();
+ GetImage()->m_ReadWriteLock.unlock();
mitkThrow() << "ImageAccessor: incoherent memory area is not supported yet";
}
return false;
}
/** \brief Uses the WaitLock to wait for another ImageAccessor*/
void mitk::ImageAccessorBase::WaitForReleaseOf(ImageAccessorWaitLock *wL)
{
- wL->m_Mutex.Lock();
+ wL->m_Mutex.lock();
// Decrement
wL->m_WaiterCount -= 1;
// If there are no more waiting ImageAccessors, delete the Mutex
// (Der Letzte macht das Licht aus!)
if (wL->m_WaiterCount <= 0)
{
- wL->m_Mutex.Unlock();
+ wL->m_Mutex.unlock();
delete wL;
}
else
{
- wL->m_Mutex.Unlock();
+ wL->m_Mutex.unlock();
}
}
void mitk::ImageAccessorBase::PreventRecursiveMutexLock(mitk::ImageAccessorBase *iAB)
{
#ifdef MITK_USE_RECURSIVE_MUTEX_PREVENTION
// Prevent deadlock
ThreadIDType id = CurrentThreadHandle();
if (CompareThreadHandles(id, iAB->m_Thread))
{
- GetImage()->m_ReadWriteLock.Unlock();
+ GetImage()->m_ReadWriteLock.unlock();
mitkThrow()
<< "Prohibited image access: the requested image part is already in use and cannot be requested recursively!";
}
#endif
}
diff --git a/Modules/Core/src/DataManagement/mitkImageDataItem.cpp b/Modules/Core/src/DataManagement/mitkImageDataItem.cpp
index cc252716cb..407ea0a0ee 100644
--- a/Modules/Core/src/DataManagement/mitkImageDataItem.cpp
+++ b/Modules/Core/src/DataManagement/mitkImageDataItem.cpp
@@ -1,334 +1,334 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkImageDataItem.h"
#include "mitkMemoryUtilities.h"
#include <vtkImageData.h>
#include <vtkPointData.h>
#include <vtkBitArray.h>
#include <vtkCharArray.h>
#include <vtkDoubleArray.h>
#include <vtkFloatArray.h>
#include <vtkIntArray.h>
#include <vtkLongArray.h>
#include <vtkShortArray.h>
#include <vtkUnsignedCharArray.h>
#include <vtkUnsignedIntArray.h>
#include <vtkUnsignedLongArray.h>
#include <vtkUnsignedShortArray.h>
#include <mitkImage.h>
#include <mitkImageVtkReadAccessor.h>
#include <mitkImageVtkWriteAccessor.h>
mitk::ImageDataItem::ImageDataItem(const ImageDataItem &aParent,
const mitk::ImageDescriptor::Pointer desc,
int timestep,
unsigned int dimension,
void *data,
bool manageMemory,
size_t offset)
: m_Data(static_cast<unsigned char *>(aParent.m_Data) + offset),
m_PixelType(new mitk::PixelType(aParent.GetPixelType())),
m_ManageMemory(false),
m_VtkImageData(nullptr),
m_VtkImageReadAccessor(nullptr),
m_VtkImageWriteAccessor(nullptr),
m_Offset(offset),
m_IsComplete(false),
m_Size(0),
m_Parent(&aParent),
m_Dimension(dimension),
m_Timestep(timestep)
{
// compute size
// const unsigned int *dims = desc->GetDimensions();
for (unsigned int i = 0; i < dimension; i++)
{
m_Dimensions[i] = desc->GetDimensions()[i];
}
this->ComputeItemSize(m_Dimensions, dimension);
if (data != nullptr && data != m_Data)
{
memcpy(m_Data, data, m_Size);
if (manageMemory)
{
delete[](unsigned char *) data;
}
}
m_ReferenceCount = 0;
}
mitk::ImageDataItem::~ImageDataItem()
{
if (m_VtkImageData != nullptr)
{
m_VtkImageData->Delete();
}
if (m_VtkImageReadAccessor != nullptr)
{
delete m_VtkImageReadAccessor;
}
if (m_VtkImageWriteAccessor != nullptr)
{
delete m_VtkImageWriteAccessor;
}
if (m_Parent.IsNull())
{
if (m_ManageMemory)
delete[] m_Data;
}
delete m_PixelType;
}
mitk::ImageDataItem::ImageDataItem(const mitk::ImageDescriptor::Pointer desc,
int timestep,
void *data,
bool manageMemory)
: m_Data(static_cast<unsigned char *>(data)),
m_PixelType(new mitk::PixelType(desc->GetChannelDescriptor(0).GetPixelType())),
m_ManageMemory(manageMemory),
m_VtkImageData(nullptr),
m_VtkImageReadAccessor(nullptr),
m_VtkImageWriteAccessor(nullptr),
m_Offset(0),
m_IsComplete(false),
m_Size(0),
m_Dimension(desc->GetNumberOfDimensions()),
m_Timestep(timestep)
{
// compute size
const unsigned int *dimensions = desc->GetDimensions();
for (unsigned int i = 0; i < m_Dimension; i++)
{
m_Dimensions[i] = dimensions[i];
}
this->ComputeItemSize(m_Dimensions, m_Dimension);
if (m_Data == nullptr)
{
m_Data = mitk::MemoryUtilities::AllocateElements<unsigned char>(m_Size);
m_ManageMemory = true;
}
m_ReferenceCount = 0;
}
mitk::ImageDataItem::ImageDataItem(const mitk::PixelType &type,
int timestep,
unsigned int dimension,
unsigned int *dimensions,
void *data,
bool manageMemory)
: m_Data(static_cast<unsigned char *>(data)),
m_PixelType(new mitk::PixelType(type)),
m_ManageMemory(manageMemory),
m_VtkImageData(nullptr),
m_VtkImageReadAccessor(nullptr),
m_VtkImageWriteAccessor(nullptr),
m_Offset(0),
m_IsComplete(false),
m_Size(0),
m_Parent(nullptr),
m_Dimension(dimension),
m_Timestep(timestep)
{
for (unsigned int i = 0; i < m_Dimension; i++)
{
m_Dimensions[i] = dimensions[i];
}
this->ComputeItemSize(dimensions, dimension);
if (m_Data == nullptr)
{
m_Data = mitk::MemoryUtilities::AllocateElements<unsigned char>(m_Size);
m_ManageMemory = true;
}
m_ReferenceCount = 0;
}
mitk::ImageDataItem::ImageDataItem(const ImageDataItem &other)
: itk::LightObject(),
m_Data(other.m_Data),
m_PixelType(new mitk::PixelType(*other.m_PixelType)),
m_ManageMemory(other.m_ManageMemory),
m_VtkImageData(nullptr),
m_VtkImageReadAccessor(nullptr),
m_VtkImageWriteAccessor(nullptr),
m_Offset(other.m_Offset),
m_IsComplete(other.m_IsComplete),
m_Size(other.m_Size),
m_Parent(other.m_Parent),
m_Dimension(other.m_Dimension),
m_Timestep(other.m_Timestep)
{
// copy m_Data ??
for (int i = 0; i < MAX_IMAGE_DIMENSIONS; ++i)
m_Dimensions[i] = other.m_Dimensions[i];
}
itk::LightObject::Pointer mitk::ImageDataItem::InternalClone() const
{
Self::Pointer newGeometry = new Self(*this);
newGeometry->UnRegister();
return newGeometry.GetPointer();
}
void mitk::ImageDataItem::ComputeItemSize(const unsigned int *dimensions, unsigned int dimension)
{
m_Size = m_PixelType->GetSize();
for (unsigned int i = 0; i < dimension; i++)
{
m_Size *= *(dimensions + i);
}
}
void mitk::ImageDataItem::ConstructVtkImageData(ImageConstPointer iP) const
{
vtkImageData *inData = vtkImageData::New();
vtkDataArray *scalars = nullptr;
const unsigned int *dims = m_Dimensions;
const unsigned int dim = m_Dimension;
unsigned long size = 0;
if (dim == 1)
{
inData->SetDimensions(dims[0] - 1, 1, 1);
size = dims[0];
inData->SetOrigin(((mitk::ScalarType)dims[0]) / 2.0, 0, 0);
}
else if (dim == 2)
{
inData->SetDimensions(dims[0], dims[1], 1);
size = dims[0] * dims[1];
inData->SetOrigin(((mitk::ScalarType)dims[0]) / 2.0f, ((mitk::ScalarType)dims[1]) / 2.0f, 0);
}
else if (dim >= 3)
{
inData->SetDimensions(dims[0], dims[1], dims[2]);
size = dims[0] * dims[1] * dims[2];
// Test
// inData->SetOrigin( (float) dims[0] / 2.0f, (float) dims[1] / 2.0f, (float) dims[2] / 2.0f );
inData->SetOrigin(0, 0, 0);
}
else
{
inData->Delete();
return;
}
if (m_Timestep >= 0)
{
SlicedGeometry3D *geom3d;
geom3d = iP->GetSlicedGeometry(m_Timestep);
const mitk::Vector3D vspacing = geom3d->GetSpacing();
double dspacing[3] = {vspacing[0], vspacing[1], vspacing[2]};
inData->SetSpacing(dspacing);
}
- if (m_PixelType->GetComponentType() == itk::ImageIOBase::CHAR)
+ if (m_PixelType->GetComponentType() == itk::IOComponentEnum::CHAR)
{
scalars = vtkCharArray::New();
}
- else if (m_PixelType->GetComponentType() == itk::ImageIOBase::UCHAR)
+ else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::UCHAR)
{
scalars = vtkUnsignedCharArray::New();
}
- else if (m_PixelType->GetComponentType() == itk::ImageIOBase::SHORT)
+ else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::SHORT)
{
scalars = vtkShortArray::New();
}
- else if (m_PixelType->GetComponentType() == itk::ImageIOBase::USHORT)
+ else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::USHORT)
{
scalars = vtkUnsignedShortArray::New();
}
- else if (m_PixelType->GetComponentType() == itk::ImageIOBase::INT)
+ else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::INT)
{
scalars = vtkIntArray::New();
}
- else if (m_PixelType->GetComponentType() == itk::ImageIOBase::UINT)
+ else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::UINT)
{
scalars = vtkUnsignedIntArray::New();
}
- else if (m_PixelType->GetComponentType() == itk::ImageIOBase::LONG)
+ else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::LONG)
{
scalars = vtkLongArray::New();
}
- else if (m_PixelType->GetComponentType() == itk::ImageIOBase::ULONG)
+ else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::ULONG)
{
scalars = vtkUnsignedLongArray::New();
}
- else if (m_PixelType->GetComponentType() == itk::ImageIOBase::FLOAT)
+ else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::FLOAT)
{
scalars = vtkFloatArray::New();
}
- else if (m_PixelType->GetComponentType() == itk::ImageIOBase::DOUBLE)
+ else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::DOUBLE)
{
scalars = vtkDoubleArray::New();
}
else
{
inData->Delete();
return;
}
m_VtkImageData = inData;
// set mitk imageDataItem void array to vtk scalar values
scalars->SetNumberOfComponents(m_PixelType->GetNumberOfComponents());
scalars->SetVoidArray(m_Data, size * m_PixelType->GetNumberOfComponents(), 1);
m_VtkImageData->GetPointData()->SetScalars(scalars);
scalars->Delete();
}
void mitk::ImageDataItem::Modified() const
{
if (m_VtkImageData)
m_VtkImageData->Modified();
}
mitk::ImageVtkReadAccessor *mitk::ImageDataItem::GetVtkImageAccessor(mitk::ImageDataItem::ImageConstPointer iP) const
{
if (m_VtkImageData == nullptr)
{
ConstructVtkImageData(iP);
}
if (m_VtkImageReadAccessor == nullptr)
{
m_VtkImageReadAccessor = new ImageVtkReadAccessor(iP, this, m_VtkImageData);
}
return m_VtkImageReadAccessor;
}
mitk::ImageVtkWriteAccessor *mitk::ImageDataItem::GetVtkImageAccessor(ImagePointer iP)
{
if (m_VtkImageData == nullptr)
{
ConstructVtkImageData(iP.GetPointer());
}
if (m_VtkImageWriteAccessor == nullptr)
{
m_VtkImageWriteAccessor = new ImageVtkWriteAccessor(iP, this, m_VtkImageData);
}
return m_VtkImageWriteAccessor;
}
diff --git a/Modules/Core/src/DataManagement/mitkImageReadAccessor.cpp b/Modules/Core/src/DataManagement/mitkImageReadAccessor.cpp
index 328e0869ee..a1a62914a6 100644
--- a/Modules/Core/src/DataManagement/mitkImageReadAccessor.cpp
+++ b/Modules/Core/src/DataManagement/mitkImageReadAccessor.cpp
@@ -1,147 +1,147 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkImageReadAccessor.h"
#include "mitkImage.h"
mitk::ImageReadAccessor::ImageReadAccessor(ImageConstPointer image, const mitk::ImageDataItem *iDI, int OptionFlags)
: ImageAccessorBase(image, iDI, OptionFlags), m_Image(image)
{
if (!(OptionFlags & ImageAccessorBase::IgnoreLock))
{
try
{
OrganizeReadAccess();
}
catch (...)
{
delete m_WaitLock;
throw;
}
}
}
mitk::ImageReadAccessor::ImageReadAccessor(ImagePointer image, const mitk::ImageDataItem *iDI, int OptionFlags)
: ImageAccessorBase(image.GetPointer(), iDI, OptionFlags), m_Image(image.GetPointer())
{
if (!(OptionFlags & ImageAccessorBase::IgnoreLock))
{
try
{
OrganizeReadAccess();
}
catch (...)
{
delete m_WaitLock;
throw;
}
}
}
mitk::ImageReadAccessor::ImageReadAccessor(const mitk::Image *image, const ImageDataItem *iDI)
: ImageAccessorBase(image, iDI, ImageAccessorBase::DefaultBehavior), m_Image(image)
{
OrganizeReadAccess();
}
mitk::ImageReadAccessor::~ImageReadAccessor()
{
if (!(m_Options & ImageAccessorBase::IgnoreLock))
{
// Future work: In case of non-coherent memory, copied area needs to be deleted
- m_Image->m_ReadWriteLock.Lock();
+ m_Image->m_ReadWriteLock.lock();
// delete self from list of ImageReadAccessors in Image
auto it = std::find(m_Image->m_Readers.begin(), m_Image->m_Readers.end(), this);
m_Image->m_Readers.erase(it);
// delete lock, if there are no waiting ImageAccessors
if (m_WaitLock->m_WaiterCount <= 0)
{
- m_WaitLock->m_Mutex.Unlock();
+ m_WaitLock->m_Mutex.unlock();
delete m_WaitLock;
}
else
{
- m_WaitLock->m_Mutex.Unlock();
+ m_WaitLock->m_Mutex.unlock();
}
- m_Image->m_ReadWriteLock.Unlock();
+ m_Image->m_ReadWriteLock.unlock();
}
else
{
delete m_WaitLock;
}
}
const mitk::Image *mitk::ImageReadAccessor::GetImage() const
{
return m_Image.GetPointer();
}
void mitk::ImageReadAccessor::OrganizeReadAccess()
{
- m_Image->m_ReadWriteLock.Lock();
+ m_Image->m_ReadWriteLock.lock();
// Check, if there is any Write-Access going on
if (m_Image->m_Writers.size() > 0)
{
// Check for every WriteAccessors, if the Region of this ImageAccessors overlaps
// make sure this iterator is not used, when m_ReadWriteLock is Unlocked!
auto it = m_Image->m_Writers.begin();
for (; it != m_Image->m_Writers.end(); ++it)
{
ImageAccessorBase *w = *it;
if (Overlap(w))
{
// An Overlap was detected. There are two possibilities to deal with this situation:
// Throw an exception or wait for the WriteAccessor w until it is released and start again with the request
// afterwards.
if (!(m_Options & ExceptionIfLocked))
{
PreventRecursiveMutexLock(w);
// WAIT
w->Increment();
- m_Image->m_ReadWriteLock.Unlock();
+ m_Image->m_ReadWriteLock.unlock();
ImageAccessorBase::WaitForReleaseOf(w->m_WaitLock);
// after waiting for the WriteAccessor w, start this method again
OrganizeReadAccess();
return;
}
else
{
// THROW EXCEPTION
- m_Image->m_ReadWriteLock.Unlock();
+ m_Image->m_ReadWriteLock.unlock();
mitkThrowException(mitk::MemoryIsLockedException)
<< "The image part being ordered by the ImageAccessor is already in use and locked";
return;
}
} // if
} // for
} // if
// Now, we know, that there is no conflict with a Write-Access
// Lock the Mutex in ImageAccessorBase, to make sure that every other ImageAccessor has to wait if it locks the mutex
- m_WaitLock->m_Mutex.Lock();
+ m_WaitLock->m_Mutex.lock();
// insert self into readers list in Image
m_Image->m_Readers.push_back(this);
// printf("ReadAccess %d %d\n",(int) m_Image->m_Readers.size(),(int) m_Image->m_Writers.size());
// fflush(0);
- m_Image->m_ReadWriteLock.Unlock();
+ m_Image->m_ReadWriteLock.unlock();
}
diff --git a/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp b/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp
index 181c995da1..811f2029c8 100644
--- a/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp
+++ b/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp
@@ -1,348 +1,348 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkImageStatisticsHolder.h"
#include "mitkHistogramGenerator.h"
#include <mitkProperties.h>
#include "mitkImageAccessByItk.h"
//#define BOUNDINGOBJECT_IGNORE
mitk::ImageStatisticsHolder::ImageStatisticsHolder(mitk::Image *image)
: m_Image(image)
{
m_CountOfMinValuedVoxels.resize(1, 0);
m_CountOfMaxValuedVoxels.resize(1, 0);
m_ScalarMin.resize(1, itk::NumericTraits<ScalarType>::max());
m_ScalarMax.resize(1, itk::NumericTraits<ScalarType>::NonpositiveMin());
m_Scalar2ndMin.resize(1, itk::NumericTraits<ScalarType>::max());
m_Scalar2ndMax.resize(1, itk::NumericTraits<ScalarType>::NonpositiveMin());
mitk::HistogramGenerator::Pointer generator = mitk::HistogramGenerator::New();
m_HistogramGeneratorObject = generator;
}
mitk::ImageStatisticsHolder::~ImageStatisticsHolder()
{
m_HistogramGeneratorObject = nullptr;
}
const mitk::ImageStatisticsHolder::HistogramType *mitk::ImageStatisticsHolder::GetScalarHistogram(
int t, unsigned int /*component*/)
{
mitk::ImageTimeSelector *timeSelector = this->GetTimeSelector();
if (timeSelector != nullptr)
{
timeSelector->SetTimeNr(t);
timeSelector->UpdateLargestPossibleRegion();
auto *generator =
static_cast<mitk::HistogramGenerator *>(m_HistogramGeneratorObject.GetPointer());
generator->SetImage(timeSelector->GetOutput());
generator->ComputeHistogram();
return static_cast<const mitk::ImageStatisticsHolder::HistogramType *>(generator->GetHistogram());
}
return nullptr;
}
bool mitk::ImageStatisticsHolder::IsValidTimeStep(int t) const
{
return m_Image->IsValidTimeStep(t);
}
mitk::ImageTimeSelector::Pointer mitk::ImageStatisticsHolder::GetTimeSelector()
{
ImageTimeSelector::Pointer timeSelector =
ImageTimeSelector::New();
timeSelector->SetInput(m_Image);
return timeSelector;
}
void mitk::ImageStatisticsHolder::Expand(unsigned int timeSteps)
{
if (!m_Image->IsValidTimeStep(timeSteps - 1))
return;
// The BaseData needs to be expanded, call the mitk::Image::Expand() method
m_Image->Expand(timeSteps);
if (timeSteps > m_ScalarMin.size())
{
m_ScalarMin.resize(timeSteps, itk::NumericTraits<ScalarType>::max());
m_ScalarMax.resize(timeSteps, itk::NumericTraits<ScalarType>::NonpositiveMin());
m_Scalar2ndMin.resize(timeSteps, itk::NumericTraits<ScalarType>::max());
m_Scalar2ndMax.resize(timeSteps, itk::NumericTraits<ScalarType>::NonpositiveMin());
m_CountOfMinValuedVoxels.resize(timeSteps, 0);
m_CountOfMaxValuedVoxels.resize(timeSteps, 0);
}
}
void mitk::ImageStatisticsHolder::ResetImageStatistics()
{
m_ScalarMin.assign(1, itk::NumericTraits<ScalarType>::max());
m_ScalarMax.assign(1, itk::NumericTraits<ScalarType>::NonpositiveMin());
m_Scalar2ndMin.assign(1, itk::NumericTraits<ScalarType>::max());
m_Scalar2ndMax.assign(1, itk::NumericTraits<ScalarType>::NonpositiveMin());
m_CountOfMinValuedVoxels.assign(1, 0);
m_CountOfMaxValuedVoxels.assign(1, 0);
}
/// \cond SKIP_DOXYGEN
template <typename ItkImageType>
void mitk::_ComputeExtremaInItkImage(const ItkImageType *itkImage, mitk::ImageStatisticsHolder *statisticsHolder, int t)
{
typename ItkImageType::RegionType region;
region = itkImage->GetBufferedRegion();
if (region.Crop(itkImage->GetRequestedRegion()) == false)
return;
if (region != itkImage->GetRequestedRegion())
return;
itk::ImageRegionConstIterator<ItkImageType> it(itkImage, region);
typedef typename ItkImageType::PixelType TPixel;
TPixel value = 0;
if (statisticsHolder == nullptr || !statisticsHolder->IsValidTimeStep(t))
return;
statisticsHolder->Expand(t + 1); // make sure we have initialized all arrays
statisticsHolder->m_CountOfMinValuedVoxels[t] = 0;
statisticsHolder->m_CountOfMaxValuedVoxels[t] = 0;
statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t] = itk::NumericTraits<ScalarType>::max();
statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t] =
itk::NumericTraits<ScalarType>::NonpositiveMin();
while (!it.IsAtEnd())
{
value = it.Get();
#ifdef BOUNDINGOBJECT_IGNORE
if (value > -32765)
{
#endif
// update min
if (value < statisticsHolder->m_ScalarMin[t])
{
statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t];
statisticsHolder->m_ScalarMin[t] = value;
statisticsHolder->m_CountOfMinValuedVoxels[t] = 1;
}
else if (value == statisticsHolder->m_ScalarMin[t])
{
++statisticsHolder->m_CountOfMinValuedVoxels[t];
}
else if (value < statisticsHolder->m_Scalar2ndMin[t])
{
statisticsHolder->m_Scalar2ndMin[t] = value;
}
// update max
if (value > statisticsHolder->m_ScalarMax[t])
{
statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t];
statisticsHolder->m_ScalarMax[t] = value;
statisticsHolder->m_CountOfMaxValuedVoxels[t] = 1;
}
else if (value == statisticsHolder->m_ScalarMax[t])
{
++statisticsHolder->m_CountOfMaxValuedVoxels[t];
}
else if (value > statisticsHolder->m_Scalar2ndMax[t])
{
statisticsHolder->m_Scalar2ndMax[t] = value;
}
#ifdef BOUNDINGOBJECT_IGNORE
}
#endif
++it;
}
//// guard for wrong 2dMin/Max on single constant value images
if (statisticsHolder->m_ScalarMax[t] == statisticsHolder->m_ScalarMin[t])
{
statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMax[t];
}
statisticsHolder->m_LastRecomputeTimeStamp.Modified();
}
/// \endcond SKIP_DOXYGEN
/// \cond SKIP_DOXYGEN
template <typename ItkImageType>
void mitk::_ComputeExtremaInItkVectorImage(const ItkImageType *itkImage,
mitk::ImageStatisticsHolder *statisticsHolder,
int t,
unsigned int component)
{
typename ItkImageType::RegionType region;
region = itkImage->GetBufferedRegion();
if (region.Crop(itkImage->GetRequestedRegion()) == false)
return;
if (region != itkImage->GetRequestedRegion())
return;
itk::ImageRegionConstIterator<ItkImageType> it(itkImage, region);
if (statisticsHolder == nullptr || !statisticsHolder->IsValidTimeStep(t))
return;
statisticsHolder->Expand(t + 1); // make sure we have initialized all arrays
statisticsHolder->m_CountOfMinValuedVoxels[t] = 0;
statisticsHolder->m_CountOfMaxValuedVoxels[t] = 0;
statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t] = itk::NumericTraits<ScalarType>::max();
statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t] =
itk::NumericTraits<ScalarType>::NonpositiveMin();
while (!it.IsAtEnd())
{
double value = it.Get()[component];
#ifdef BOUNDINGOBJECT_IGNORE
if (value > -32765)
{
#endif
// update min
if (value < statisticsHolder->m_ScalarMin[t])
{
statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t];
statisticsHolder->m_ScalarMin[t] = value;
statisticsHolder->m_CountOfMinValuedVoxels[t] = 1;
}
else if (value == statisticsHolder->m_ScalarMin[t])
{
++statisticsHolder->m_CountOfMinValuedVoxels[t];
}
else if (value < statisticsHolder->m_Scalar2ndMin[t])
{
statisticsHolder->m_Scalar2ndMin[t] = value;
}
// update max
if (value > statisticsHolder->m_ScalarMax[t])
{
statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t];
statisticsHolder->m_ScalarMax[t] = value;
statisticsHolder->m_CountOfMaxValuedVoxels[t] = 1;
}
else if (value == statisticsHolder->m_ScalarMax[t])
{
++statisticsHolder->m_CountOfMaxValuedVoxels[t];
}
else if (value > statisticsHolder->m_Scalar2ndMax[t])
{
statisticsHolder->m_Scalar2ndMax[t] = value;
}
#ifdef BOUNDINGOBJECT_IGNORE
}
#endif
++it;
}
//// guard for wrong 2dMin/Max on single constant value images
if (statisticsHolder->m_ScalarMax[t] == statisticsHolder->m_ScalarMin[t])
{
statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMax[t];
}
statisticsHolder->m_LastRecomputeTimeStamp.Modified();
}
/// \endcond SKIP_DOXYGEN
void mitk::ImageStatisticsHolder::ComputeImageStatistics(int t, unsigned int component)
{
// timestep valid?
if (!m_Image->IsValidTimeStep(t))
return;
// image modified?
if (this->m_Image->GetMTime() > m_LastRecomputeTimeStamp.GetMTime())
this->ResetImageStatistics();
Expand(t + 1);
// do we have valid information already?
if (m_ScalarMin[t] != itk::NumericTraits<ScalarType>::max() ||
m_Scalar2ndMin[t] != itk::NumericTraits<ScalarType>::max())
return; // Values already calculated before...
// used to avoid statistics calculation on Odf images. property will be replaced as soons as bug 17928 is merged and
// the diffusion image refactoring is complete.
mitk::BoolProperty *isSh = dynamic_cast<mitk::BoolProperty *>(m_Image->GetProperty("IsShImage").GetPointer());
mitk::BoolProperty *isOdf = dynamic_cast<mitk::BoolProperty *>(m_Image->GetProperty("IsOdfImage").GetPointer());
const mitk::PixelType pType = m_Image->GetPixelType(0);
- if (pType.GetNumberOfComponents() == 1 && (pType.GetPixelType() != itk::ImageIOBase::UNKNOWNPIXELTYPE) &&
- (pType.GetPixelType() != itk::ImageIOBase::VECTOR))
+ if (pType.GetNumberOfComponents() == 1 && (pType.GetPixelType() != itk::IOPixelEnum::UNKNOWNPIXELTYPE) &&
+ (pType.GetPixelType() != itk::IOPixelEnum::VECTOR))
{
// recompute
mitk::ImageTimeSelector::Pointer timeSelector = this->GetTimeSelector();
if (timeSelector.IsNotNull())
{
timeSelector->SetTimeNr(t);
timeSelector->UpdateLargestPossibleRegion();
const mitk::Image *image = timeSelector->GetOutput();
AccessByItk_2(image, _ComputeExtremaInItkImage, this, t);
}
}
- else if (pType.GetPixelType() == itk::ImageIOBase::VECTOR &&
+ else if (pType.GetPixelType() == itk::IOPixelEnum::VECTOR &&
(!isOdf || !isOdf->GetValue()) && (!isSh || !isSh->GetValue())) // we have a vector image
{
// recompute
mitk::ImageTimeSelector::Pointer timeSelector = this->GetTimeSelector();
if (timeSelector.IsNotNull())
{
timeSelector->SetTimeNr(t);
timeSelector->UpdateLargestPossibleRegion();
const mitk::Image *image = timeSelector->GetOutput();
AccessVectorPixelTypeByItk_n(image, _ComputeExtremaInItkVectorImage, (this, t, component));
}
}
else
{
m_ScalarMin[t] = 0;
m_ScalarMax[t] = 255;
m_Scalar2ndMin[t] = 0;
m_Scalar2ndMax[t] = 255;
}
}
mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValueMin(int t, unsigned int component)
{
ComputeImageStatistics(t, component);
return m_ScalarMin[t];
}
mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValueMax(int t, unsigned int component)
{
ComputeImageStatistics(t, component);
return m_ScalarMax[t];
}
mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValue2ndMin(int t, unsigned int component)
{
ComputeImageStatistics(t, component);
return m_Scalar2ndMin[t];
}
mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValue2ndMax(int t, unsigned int component)
{
ComputeImageStatistics(t, component);
return m_Scalar2ndMax[t];
}
mitk::ScalarType mitk::ImageStatisticsHolder::GetCountOfMinValuedVoxels(int t, unsigned int component)
{
ComputeImageStatistics(t, component);
return m_CountOfMinValuedVoxels[t];
}
mitk::ScalarType mitk::ImageStatisticsHolder::GetCountOfMaxValuedVoxels(int t, unsigned int component)
{
ComputeImageStatistics(t, component);
return m_CountOfMaxValuedVoxels[t];
}
diff --git a/Modules/Core/src/DataManagement/mitkImageVtkReadAccessor.cpp b/Modules/Core/src/DataManagement/mitkImageVtkReadAccessor.cpp
index acc2cdbd24..49335d337b 100644
--- a/Modules/Core/src/DataManagement/mitkImageVtkReadAccessor.cpp
+++ b/Modules/Core/src/DataManagement/mitkImageVtkReadAccessor.cpp
@@ -1,54 +1,54 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkImageVtkReadAccessor.h"
#include "mitkImage.h"
#include <vtkImageData.h>
const mitk::Image *mitk::ImageVtkReadAccessor::GetImage() const
{
return m_Image;
}
mitk::ImageVtkReadAccessor::ImageVtkReadAccessor(ImageConstPointer iP,
const mitk::ImageDataItem *iDI,
const vtkImageData *imageDataVtk)
: ImageAccessorBase(iP, iDI), m_Image(iP.GetPointer()), m_ImageDataVtk(imageDataVtk)
{
- m_Image->m_VtkReadersLock.Lock();
+ m_Image->m_VtkReadersLock.lock();
m_Image->m_VtkReaders.push_back(this);
// printf("m_VtkReaders.size(): %d\n", (int) m_Image->m_VtkReaders.size());
- m_Image->m_VtkReadersLock.Unlock();
+ m_Image->m_VtkReadersLock.unlock();
}
mitk::ImageVtkReadAccessor::~ImageVtkReadAccessor()
{
- m_Image->m_VtkReadersLock.Lock();
+ m_Image->m_VtkReadersLock.lock();
auto it = std::find(m_Image->m_VtkReaders.begin(), m_Image->m_VtkReaders.end(), this);
if (it != m_Image->m_VtkReaders.end())
{
m_Image->m_VtkReaders.erase(it);
}
// printf("m_VtkReaders.size(): %d\n", (int) m_Image->m_VtkReaders.size());
- m_Image->m_VtkReadersLock.Unlock();
+ m_Image->m_VtkReadersLock.unlock();
}
const vtkImageData *mitk::ImageVtkReadAccessor::GetVtkImageData() const
{
return m_ImageDataVtk;
}
diff --git a/Modules/Core/src/DataManagement/mitkImageVtkWriteAccessor.cpp b/Modules/Core/src/DataManagement/mitkImageVtkWriteAccessor.cpp
index 8d860f03e1..15fc124e00 100644
--- a/Modules/Core/src/DataManagement/mitkImageVtkWriteAccessor.cpp
+++ b/Modules/Core/src/DataManagement/mitkImageVtkWriteAccessor.cpp
@@ -1,54 +1,54 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkImageVtkWriteAccessor.h"
#include "mitkImage.h"
#include <vtkImageData.h>
const mitk::Image *mitk::ImageVtkWriteAccessor::GetImage() const
{
return m_Image;
}
mitk::ImageVtkWriteAccessor::ImageVtkWriteAccessor(ImagePointer iP,
const mitk::ImageDataItem *iDI,
vtkImageData *imageDataVtk)
: ImageAccessorBase(nullptr, iDI), m_Image(iP.GetPointer()), m_ImageDataVtk(imageDataVtk)
{
- m_Image->m_VtkReadersLock.Lock();
+ m_Image->m_VtkReadersLock.lock();
m_Image->m_VtkReaders.push_back(this);
// printf("m_VtkReaders.size(): %d\n", (int) m_Image->m_VtkReaders.size());
- m_Image->m_VtkReadersLock.Unlock();
+ m_Image->m_VtkReadersLock.unlock();
}
mitk::ImageVtkWriteAccessor::~ImageVtkWriteAccessor()
{
- m_Image->m_VtkReadersLock.Lock();
+ m_Image->m_VtkReadersLock.lock();
auto it = std::find(m_Image->m_VtkReaders.begin(), m_Image->m_VtkReaders.end(), this);
if (it != m_Image->m_VtkReaders.end())
{
m_Image->m_VtkReaders.erase(it);
}
// printf("m_VtkReaders.size(): %d\n", (int) m_Image->m_VtkReaders.size());
- m_Image->m_VtkReadersLock.Unlock();
+ m_Image->m_VtkReadersLock.unlock();
}
vtkImageData *mitk::ImageVtkWriteAccessor::GetVtkImageData() const
{
return m_ImageDataVtk;
}
diff --git a/Modules/Core/src/DataManagement/mitkImageWriteAccessor.cpp b/Modules/Core/src/DataManagement/mitkImageWriteAccessor.cpp
index c03cbe3db3..ea906399bd 100644
--- a/Modules/Core/src/DataManagement/mitkImageWriteAccessor.cpp
+++ b/Modules/Core/src/DataManagement/mitkImageWriteAccessor.cpp
@@ -1,147 +1,147 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkImageWriteAccessor.h"
mitk::ImageWriteAccessor::ImageWriteAccessor(ImagePointer image, const mitk::ImageDataItem *iDI, int OptionFlags)
: ImageAccessorBase(image.GetPointer(), iDI, OptionFlags), m_Image(image)
{
OrganizeWriteAccess();
}
mitk::ImageWriteAccessor::~ImageWriteAccessor()
{
// In case of non-coherent memory, copied area needs to be written back
// TODO
- m_Image->m_ReadWriteLock.Lock();
+ m_Image->m_ReadWriteLock.lock();
// delete self from list of ImageReadAccessors in Image
auto it = std::find(m_Image->m_Writers.begin(), m_Image->m_Writers.end(), this);
m_Image->m_Writers.erase(it);
// delete lock, if there are no waiting ImageAccessors
if (m_WaitLock->m_WaiterCount <= 0)
{
- m_WaitLock->m_Mutex.Unlock();
+ m_WaitLock->m_Mutex.unlock();
delete m_WaitLock;
}
else
{
- m_WaitLock->m_Mutex.Unlock();
+ m_WaitLock->m_Mutex.unlock();
}
- m_Image->m_ReadWriteLock.Unlock();
+ m_Image->m_ReadWriteLock.unlock();
}
const mitk::Image *mitk::ImageWriteAccessor::GetImage() const
{
return m_Image.GetPointer();
}
void mitk::ImageWriteAccessor::OrganizeWriteAccess()
{
- m_Image->m_ReadWriteLock.Lock();
+ m_Image->m_ReadWriteLock.lock();
bool readOverlap = false;
bool writeOverlap = false;
ImageAccessorWaitLock *overlapLock = nullptr;
// Check, if there is any Read-Access going on
if (m_Image->m_Readers.size() > 0)
{
// Check for every ReadAccessor, if the Region of this ImageAccessors overlaps
// make sure this iterator is not used, when m_ReadWriteLock is Unlocked!
auto it = m_Image->m_Readers.begin();
for (; it != m_Image->m_Readers.end(); ++it)
{
ImageAccessorBase *r = *it;
if ((r->m_Options & IgnoreLock) == 0 && Overlap(r))
{
// An Overlap was detected.
PreventRecursiveMutexLock(r);
readOverlap = true;
overlapLock = r->m_WaitLock;
break;
} // if
} // for
} // if
// Check, if there is any Write-Access going on
if (m_Image->m_Writers.size() > 0)
{
// Check for every WriteAccessor, if the Region of this ImageAccessors overlaps
// make sure this iterator is not used, when m_ReadWriteLock is Unlocked!
auto it = m_Image->m_Writers.begin();
for (; it != m_Image->m_Writers.end(); ++it)
{
ImageAccessorBase *w = *it;
if ((w->m_Options & IgnoreLock) == 0 && Overlap(w))
{
// An Overlap was detected.
PreventRecursiveMutexLock(w);
// save overlapping Waitlock
writeOverlap = true;
overlapLock = w->m_WaitLock;
break;
} // if
} // for
} // if
if (readOverlap || writeOverlap)
{
// Throw an exception or wait for the WriteAccessor w until it is released and start again with the request
// afterwards.
if (!(m_Options & ExceptionIfLocked))
{
// WAIT
overlapLock->m_WaiterCount += 1;
- m_Image->m_ReadWriteLock.Unlock();
+ m_Image->m_ReadWriteLock.unlock();
ImageAccessorBase::WaitForReleaseOf(overlapLock);
// after waiting for the ImageAccessor, start this method again
OrganizeWriteAccess();
return;
}
else
{
// THROW EXCEPTION
- m_Image->m_ReadWriteLock.Unlock();
+ m_Image->m_ReadWriteLock.unlock();
mitkThrowException(mitk::MemoryIsLockedException)
<< "The image part being ordered by the ImageAccessor is already in use and locked";
// MITK_ERROR("Speicherbereich belegt");
return;
}
}
// Now, we know, that there is no conflict with a Read- or Write-Access
// Lock the Mutex in ImageAccessorBase, to make sure that every other ImageAccessor has to wait
- m_WaitLock->m_Mutex.Lock();
+ m_WaitLock->m_Mutex.lock();
// insert self into Writers list in Image
m_Image->m_Writers.push_back(this);
// printf("WriteAccess %d %d\n",(int) m_Image->m_Readers.size(),(int) m_Image->m_Writers.size());
// fflush(0);
- m_Image->m_ReadWriteLock.Unlock();
+ m_Image->m_ReadWriteLock.unlock();
}
diff --git a/Modules/Core/src/DataManagement/mitkLevelWindow.cpp b/Modules/Core/src/DataManagement/mitkLevelWindow.cpp
index 6da6ebc0b1..6ef243cb93 100644
--- a/Modules/Core/src/DataManagement/mitkLevelWindow.cpp
+++ b/Modules/Core/src/DataManagement/mitkLevelWindow.cpp
@@ -1,507 +1,507 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkLevelWindow.h"
#include "mitkImage.h"
#include "mitkImageSliceSelector.h"
#include "mitkImageStatisticsHolder.h"
#include <algorithm>
void mitk::LevelWindow::EnsureConsistency()
{
// Check if total range is ok
{
if (m_RangeMin > m_RangeMax)
std::swap(m_RangeMin, m_RangeMax);
if (m_RangeMin == m_RangeMax)
m_RangeMin = m_RangeMax - 1;
}
// Check if current window is ok
{
if (m_LowerWindowBound > m_UpperWindowBound)
std::swap(m_LowerWindowBound, m_UpperWindowBound);
if (m_LowerWindowBound <= m_RangeMin)
m_LowerWindowBound = m_RangeMin;
if (m_UpperWindowBound <= m_RangeMin)
m_UpperWindowBound = m_RangeMin + 1;
if (m_LowerWindowBound >= m_RangeMax)
m_LowerWindowBound = m_RangeMax - 1;
if (m_UpperWindowBound >= m_RangeMax)
m_UpperWindowBound = m_RangeMax;
if (m_LowerWindowBound == m_UpperWindowBound)
{
m_UpperWindowBound += 0.5;
m_LowerWindowBound -= 0.5;
m_UpperWindowBound = std::min(m_UpperWindowBound, m_RangeMax);
m_LowerWindowBound = std::max(m_LowerWindowBound, m_RangeMin);
}
}
}
mitk::LevelWindow::LevelWindow(mitk::ScalarType level, mitk::ScalarType window)
: m_LowerWindowBound(level - window / 2.0),
m_UpperWindowBound(level + window / 2.0),
m_RangeMin(-2048.0),
m_RangeMax(4096.0),
m_DefaultLowerBound(-2048.0),
m_DefaultUpperBound(4096.0),
m_IsFloatingImage(false),
m_Fixed(false)
{
SetDefaultLevelWindow(level, window);
SetLevelWindow(level, window, true);
}
mitk::LevelWindow::LevelWindow(const mitk::LevelWindow &levWin)
: m_LowerWindowBound(levWin.GetLowerWindowBound()),
m_UpperWindowBound(levWin.GetUpperWindowBound()),
m_RangeMin(levWin.GetRangeMin()),
m_RangeMax(levWin.GetRangeMax()),
m_DefaultLowerBound(levWin.GetDefaultLowerBound()),
m_DefaultUpperBound(levWin.GetDefaultUpperBound()),
m_IsFloatingImage(levWin.IsFloatingValues()),
m_Fixed(levWin.GetFixed())
{
}
mitk::LevelWindow::~LevelWindow()
{
}
mitk::ScalarType mitk::LevelWindow::GetLevel() const
{
return (m_UpperWindowBound - m_LowerWindowBound) / 2.0 + m_LowerWindowBound;
}
mitk::ScalarType mitk::LevelWindow::GetWindow() const
{
return (m_UpperWindowBound - m_LowerWindowBound);
}
mitk::ScalarType mitk::LevelWindow::GetDefaultLevel() const
{
return ((m_DefaultUpperBound + m_DefaultLowerBound) / 2.0);
}
mitk::ScalarType mitk::LevelWindow::GetDefaultWindow() const
{
return ((m_DefaultUpperBound - m_DefaultLowerBound));
}
void mitk::LevelWindow::ResetDefaultLevelWindow()
{
SetLevelWindow(GetDefaultLevel(), GetDefaultWindow());
}
mitk::ScalarType mitk::LevelWindow::GetLowerWindowBound() const
{
return m_LowerWindowBound;
}
mitk::ScalarType mitk::LevelWindow::GetUpperWindowBound() const
{
return m_UpperWindowBound;
}
void mitk::LevelWindow::SetDefaultLevelWindow(mitk::ScalarType level, mitk::ScalarType window)
{
SetDefaultBoundaries((level - (window / 2.0)), (level + (window / 2.0)));
}
void mitk::LevelWindow::SetLevelWindow(mitk::ScalarType level, mitk::ScalarType window, bool expandRangesIfNecessary)
{
SetWindowBounds((level - (window / 2.0)), (level + (window / 2.0)), expandRangesIfNecessary);
}
void mitk::LevelWindow::SetWindowBounds(mitk::ScalarType lowerBound,
mitk::ScalarType upperBound,
bool expandRangesIfNecessary)
{
if (IsFixed())
return;
m_LowerWindowBound = lowerBound;
m_UpperWindowBound = upperBound;
if (expandRangesIfNecessary)
{
/* if caller is sure he wants exactly that level/window, we make sure the limits match */
if (m_LowerWindowBound > m_UpperWindowBound)
std::swap(m_LowerWindowBound, m_UpperWindowBound);
if (m_LowerWindowBound < m_RangeMin)
{
m_RangeMin = m_LowerWindowBound;
}
if (m_UpperWindowBound > m_RangeMax)
{
m_RangeMax = m_UpperWindowBound;
}
}
EnsureConsistency();
}
void mitk::LevelWindow::SetRangeMinMax(mitk::ScalarType min, mitk::ScalarType max)
{
if (IsFixed())
return;
m_RangeMin = min;
m_RangeMax = max;
EnsureConsistency();
}
void mitk::LevelWindow::SetDefaultBoundaries(mitk::ScalarType low, mitk::ScalarType up)
{
if (IsFixed())
return;
m_DefaultLowerBound = low;
m_DefaultUpperBound = up;
// Check if default window is ok
{
if (m_DefaultLowerBound > m_DefaultUpperBound)
std::swap(m_DefaultLowerBound, m_DefaultUpperBound);
if (m_DefaultLowerBound == m_DefaultUpperBound)
m_DefaultLowerBound--;
}
EnsureConsistency();
}
void mitk::LevelWindow::SetToMaxWindowSize()
{
SetWindowBounds(m_RangeMin, m_RangeMax);
}
mitk::ScalarType mitk::LevelWindow::GetRangeMin() const
{
return m_RangeMin;
}
mitk::ScalarType mitk::LevelWindow::GetRangeMax() const
{
return m_RangeMax;
}
mitk::ScalarType mitk::LevelWindow::GetRange() const
{
return m_RangeMax - m_RangeMin;
}
mitk::ScalarType mitk::LevelWindow::GetDefaultUpperBound() const
{
return m_DefaultUpperBound;
}
mitk::ScalarType mitk::LevelWindow::GetDefaultLowerBound() const
{
return m_DefaultLowerBound;
}
void mitk::LevelWindow::ResetDefaultRangeMinMax()
{
SetRangeMinMax(m_DefaultLowerBound, m_DefaultUpperBound);
}
/*!
This method initializes a mitk::LevelWindow from an mitk::Image. The algorithm is as follows:
Default to taking the central image slice for quick analysis.
Compute the smallest (minValue), second smallest (min2ndValue), second largest (max2ndValue), and
largest (maxValue) data value by traversing the pixel values only once. In the
same scan it also computes the count of minValue values and maxValue values.
After that a basic histogram with specific information about the
extrems is complete.
If minValue == maxValue, the center slice is uniform and the above scan is repeated for
the complete image, not just one slice
Next, special cases of images with only 1, 2 or 3 distinct data values
have hand assigned level window ranges.
Next the level window is set relative to the inner range IR = lengthOf([min2ndValue, max2ndValue])
For count(minValue) > 20% the smallest values are frequent and should be
distinct from the min2ndValue and larger values (minValue may be std:min, may signify
something special) hence the lower end of the level window is set to min2ndValue - 0.5 * IR
For count(minValue) <= 20% the smallest values are not so important and can
blend with the next ones => min(level window) = min2ndValue
And analog for max(level window):
count(max2ndValue) > 20%: max(level window) = max2ndValue + 0.5 * IR
count(max2ndValue) < 20%: max(level window) = max2ndValue
In both 20%+ cases the level window bounds are clamped to the [minValue, maxValue] range
In consequence the level window maximizes contrast with minimal amount of
computation and does do useful things if the data contains std::min or
std:max values or has only 1 or 2 or 3 data values.
*/
void mitk::LevelWindow::SetAuto(const mitk::Image *image,
bool /*tryPicTags*/,
bool guessByCentralSlice,
unsigned selectedComponent)
{
if (IsFixed())
return;
if (image == nullptr || !image->IsInitialized())
return;
- if (itk::ImageIOBase::IOComponentType::FLOAT == image->GetPixelType().GetComponentType()
- || itk::ImageIOBase::IOComponentType::DOUBLE == image->GetPixelType().GetComponentType())
+ if (itk::IOComponentEnum::FLOAT == image->GetPixelType().GetComponentType()
+ || itk::IOComponentEnum::DOUBLE == image->GetPixelType().GetComponentType())
{
m_IsFloatingImage = true;
}
else
{
m_IsFloatingImage = false;
}
const mitk::Image *wholeImage = image;
ScalarType minValue = 0.0;
ScalarType maxValue = 0.0;
ScalarType min2ndValue = 0.0;
ScalarType max2ndValue = 0.0;
mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New();
if (guessByCentralSlice)
{
sliceSelector->SetInput(image);
sliceSelector->SetSliceNr(image->GetDimension(2) / 2);
sliceSelector->SetTimeNr(image->GetDimension(3) / 2);
sliceSelector->SetChannelNr(image->GetDimension(4) / 2);
sliceSelector->Update();
image = sliceSelector->GetOutput();
if (image == nullptr || !image->IsInitialized())
return;
minValue = image->GetStatistics()->GetScalarValueMin(0, selectedComponent);
maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute();
min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute();
max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute();
if (minValue == maxValue)
{
// guessByCentralSlice seems to have failed, lets look at all data
image = wholeImage;
minValue = image->GetStatistics()->GetScalarValueMin(0, selectedComponent);
maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute();
min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute();
max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute();
}
}
else
{
const_cast<Image *>(image)->Update();
minValue = image->GetStatistics()->GetScalarValueMin(0, selectedComponent);
maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(0);
min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(0);
max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(0);
for (unsigned int i = 1; i < image->GetDimension(3); ++i)
{
ScalarType minValueTemp = image->GetStatistics()->GetScalarValueMin(i, selectedComponent);
if (minValue > minValueTemp)
minValue = minValueTemp;
ScalarType maxValueTemp = image->GetStatistics()->GetScalarValueMaxNoRecompute(i);
if (maxValue < maxValueTemp)
maxValue = maxValueTemp;
ScalarType min2ndValueTemp = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(i);
if (min2ndValue > min2ndValueTemp)
min2ndValue = min2ndValueTemp;
ScalarType max2ndValueTemp = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(i);
if (max2ndValue > max2ndValueTemp)
max2ndValue = max2ndValueTemp;
}
}
// Fix for bug# 344 Level Window wird bei Eris Cut bildern nicht richtig gesetzt
- if (image->GetPixelType().GetPixelType() == itk::ImageIOBase::SCALAR &&
- image->GetPixelType().GetComponentType() == itk::ImageIOBase::INT && image->GetPixelType().GetBpe() >= 8)
+ if (image->GetPixelType().GetPixelType() == itk::IOPixelEnum::SCALAR &&
+ image->GetPixelType().GetComponentType() == itk::IOComponentEnum::INT && image->GetPixelType().GetBpe() >= 8)
{
// the windows compiler complains about ambiguos 'pow' call, therefore static casting to (double, int)
if (minValue == -(pow((double)2.0, static_cast<int>(image->GetPixelType().GetBpe() / 2))))
{
minValue = min2ndValue;
}
}
// End fix
//// uniform image
if (minValue == maxValue)
{
minValue = maxValue - 1;
}
else
{
// Due to bug #8690 level window now is no longer of fixed range by default but the range adapts according to
// levelwindow interaction
// This is done because the range should be a little bit larger from the beginning so that the scale doesn't start
// to resize right from the beginning
double additionalRange = 0.15 * (maxValue - minValue);
minValue -= additionalRange;
maxValue += additionalRange;
}
if (!std::isfinite(minValue))
{
minValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(0);
}
if (!std::isfinite(maxValue))
{
maxValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(0);
}
SetRangeMinMax(minValue, maxValue);
SetDefaultBoundaries(minValue, maxValue);
size_t numPixelsInDataset = image->GetDimensions()[0];
for (decltype(image->GetDimension()) k = 1; k < image->GetDimension(); ++k)
numPixelsInDataset *= image->GetDimensions()[k];
const auto minCount = image->GetStatistics()->GetCountOfMinValuedVoxelsNoRecompute();
const auto maxCount = image->GetStatistics()->GetCountOfMaxValuedVoxelsNoRecompute();
const auto minCountFraction = minCount / static_cast<ScalarType>(numPixelsInDataset);
const auto maxCountFraction = maxCount / static_cast<ScalarType>(numPixelsInDataset);
//// binary image
if (min2ndValue == maxValue)
{
// noop; full range is fine
}
//// triple value image, put middle value in center of gray level ramp
else if (min2ndValue == max2ndValue)
{
ScalarType minDelta = std::min(min2ndValue - minValue, maxValue - min2ndValue);
minValue = min2ndValue - minDelta;
maxValue = min2ndValue + minDelta;
}
// now we can assume more than three distict scalar values
else
{
ScalarType innerRange = max2ndValue - min2ndValue;
if (minCountFraction > 0.2) //// lots of min values -> make different from rest, but not miles away
{
ScalarType halfInnerRangeGapMinValue = min2ndValue - innerRange / 2.0;
minValue = std::max(minValue, halfInnerRangeGapMinValue);
}
else //// few min values -> focus on innerRange
{
minValue = min2ndValue;
}
if (maxCountFraction > 0.2) //// lots of max values -> make different from rest
{
ScalarType halfInnerRangeGapMaxValue = max2ndValue + innerRange / 2.0;
maxValue = std::min(maxValue, halfInnerRangeGapMaxValue);
}
else //// few max values -> focus on innerRange
{
maxValue = max2ndValue;
}
}
SetWindowBounds(minValue, maxValue);
SetDefaultLevelWindow((maxValue - minValue) / 2 + minValue, maxValue - minValue);
}
void mitk::LevelWindow::SetToImageRange(const mitk::Image *image)
{
if (IsFixed())
return;
if (image == nullptr || !image->IsInitialized())
return;
ScalarType minValue = image->GetStatistics()->GetScalarValueMin(0);
if (!std::isfinite(minValue))
{
minValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(0);
}
ScalarType maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(0);
if (!std::isfinite(maxValue))
{
maxValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(0);
}
SetRangeMinMax(minValue, maxValue);
SetDefaultBoundaries(minValue, maxValue);
SetWindowBounds(minValue, maxValue);
SetDefaultLevelWindow((maxValue - minValue) / 2 + minValue, maxValue - minValue);
}
void mitk::LevelWindow::SetFixed(bool fixed)
{
m_Fixed = fixed;
}
bool mitk::LevelWindow::GetFixed() const
{
return m_Fixed;
}
bool mitk::LevelWindow::IsFixed() const
{
return m_Fixed;
}
bool mitk::LevelWindow::IsFloatingValues() const
{
return m_IsFloatingImage;
}
void mitk::LevelWindow::SetFloatingValues(bool value)
{
m_IsFloatingImage = value;
}
bool mitk::LevelWindow::operator==(const mitk::LevelWindow &levWin) const
{
return mitk::Equal(this->m_RangeMin, levWin.m_RangeMin, mitk::sqrteps) &&
mitk::Equal(this->m_RangeMax, levWin.m_RangeMax, mitk::sqrteps) &&
mitk::Equal(this->m_DefaultLowerBound, levWin.m_DefaultLowerBound, mitk::sqrteps) &&
mitk::Equal(this->m_DefaultUpperBound, levWin.m_DefaultUpperBound, mitk::sqrteps) &&
mitk::Equal(this->m_LowerWindowBound, levWin.m_LowerWindowBound, mitk::sqrteps) &&
mitk::Equal(this->m_UpperWindowBound, levWin.m_UpperWindowBound, mitk::sqrteps) &&
m_Fixed == levWin.IsFixed() && m_IsFloatingImage == levWin.IsFloatingValues();
}
bool mitk::LevelWindow::operator!=(const mitk::LevelWindow &levWin) const
{
return !((*this) == levWin);
}
mitk::LevelWindow &mitk::LevelWindow::operator=(const mitk::LevelWindow &levWin)
{
if (this == &levWin)
{
return *this;
}
else
{
m_RangeMin = levWin.GetRangeMin();
m_RangeMax = levWin.GetRangeMax();
m_LowerWindowBound = levWin.GetLowerWindowBound();
m_UpperWindowBound = levWin.GetUpperWindowBound();
m_DefaultLowerBound = levWin.GetDefaultLowerBound();
m_DefaultUpperBound = levWin.GetDefaultUpperBound();
m_Fixed = levWin.GetFixed();
m_IsFloatingImage = levWin.IsFloatingValues();
return *this;
}
}
diff --git a/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp b/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp
index 4a6565d4e7..013c257025 100644
--- a/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp
+++ b/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp
@@ -1,971 +1,973 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkPlaneGeometry.h"
#include "mitkInteractionConst.h"
#include "mitkLine.h"
#include "mitkPlaneOperation.h"
#include <itkSpatialOrientationAdapter.h>
#include <vtkTransform.h>
#include <vnl/vnl_cross.h>
namespace mitk
{
PlaneGeometry::PlaneGeometry() : Superclass(), m_ReferenceGeometry(nullptr) { Initialize(); }
PlaneGeometry::~PlaneGeometry() {}
PlaneGeometry::PlaneGeometry(const PlaneGeometry &other)
: Superclass(other), m_ReferenceGeometry(other.m_ReferenceGeometry)
{
}
bool PlaneGeometry::CheckRotationMatrix(mitk::AffineTransform3D *transform, double epsilon)
{
bool rotation = true;
auto matrix = transform->GetMatrix().GetVnlMatrix();
matrix.normalize_columns();
auto det = vnl_determinant(matrix);
if (fabs(det-1.0) > epsilon)
{
MITK_WARN << "Invalid rotation matrix! Determinant != 1 (" << det << ")";
rotation = false;
}
vnl_matrix_fixed<double, 3, 3> id; id.set_identity();
auto should_be_id = matrix*matrix.transpose();
should_be_id -= id;
auto max = should_be_id.absolute_value_max();
if (max > epsilon)
{
MITK_WARN << "Invalid rotation matrix! R*R^T != ID. Max value: " << max << " (should be 0)";
rotation = false;
}
return rotation;
}
void PlaneGeometry::CheckIndexToWorldTransform(mitk::AffineTransform3D *transform)
{
this->CheckRotationMatrix(transform);
}
void PlaneGeometry::CheckBounds(const BoundingBox::BoundsArrayType &bounds)
{
// error: unused parameter 'bounds'
// this happens in release mode, where the assert macro is defined empty
// hence we "use" the parameter:
(void)bounds;
// currently the unit rectangle must be starting at the origin [0,0]
assert(bounds[0] == 0);
assert(bounds[2] == 0);
// the unit rectangle must be two-dimensional
assert(bounds[1] > 0);
assert(bounds[3] > 0);
}
void PlaneGeometry::IndexToWorld(const Point2D &pt_units, Point2D &pt_mm) const
{
pt_mm[0] = GetExtentInMM(0) / GetExtent(0) * pt_units[0];
pt_mm[1] = GetExtentInMM(1) / GetExtent(1) * pt_units[1];
}
void PlaneGeometry::WorldToIndex(const Point2D &pt_mm, Point2D &pt_units) const
{
pt_units[0] = pt_mm[0] * (1.0 / (GetExtentInMM(0) / GetExtent(0)));
pt_units[1] = pt_mm[1] * (1.0 / (GetExtentInMM(1) / GetExtent(1)));
}
void PlaneGeometry::IndexToWorld(const Point2D & /*atPt2d_units*/, const Vector2D &vec_units, Vector2D &vec_mm) const
{
MITK_WARN << "Warning! Call of the deprecated function PlaneGeometry::IndexToWorld(point, vec, vec). Use "
"PlaneGeometry::IndexToWorld(vec, vec) instead!";
this->IndexToWorld(vec_units, vec_mm);
}
void PlaneGeometry::IndexToWorld(const Vector2D &vec_units, Vector2D &vec_mm) const
{
vec_mm[0] = (GetExtentInMM(0) / GetExtent(0)) * vec_units[0];
vec_mm[1] = (GetExtentInMM(1) / GetExtent(1)) * vec_units[1];
}
void PlaneGeometry::WorldToIndex(const Point2D & /*atPt2d_mm*/, const Vector2D &vec_mm, Vector2D &vec_units) const
{
MITK_WARN << "Warning! Call of the deprecated function PlaneGeometry::WorldToIndex(point, vec, vec). Use "
"PlaneGeometry::WorldToIndex(vec, vec) instead!";
this->WorldToIndex(vec_mm, vec_units);
}
void PlaneGeometry::WorldToIndex(const Vector2D &vec_mm, Vector2D &vec_units) const
{
vec_units[0] = vec_mm[0] * (1.0 / (GetExtentInMM(0) / GetExtent(0)));
vec_units[1] = vec_mm[1] * (1.0 / (GetExtentInMM(1) / GetExtent(1)));
}
void PlaneGeometry::InitializeStandardPlane(mitk::ScalarType width,
ScalarType height,
const Vector3D &spacing,
PlaneGeometry::PlaneOrientation planeorientation,
ScalarType zPosition,
bool frontside,
bool rotated,
bool top)
{
AffineTransform3D::Pointer transform;
transform = AffineTransform3D::New();
AffineTransform3D::MatrixType matrix;
AffineTransform3D::MatrixType::InternalMatrixType &vnlmatrix = matrix.GetVnlMatrix();
vnlmatrix.set_identity();
vnlmatrix(0, 0) = spacing[0];
vnlmatrix(1, 1) = spacing[1];
vnlmatrix(2, 2) = spacing[2];
transform->SetIdentity();
transform->SetMatrix(matrix);
InitializeStandardPlane(width, height, transform.GetPointer(), planeorientation, zPosition, frontside, rotated, top);
}
void PlaneGeometry::InitializeStandardPlane(mitk::ScalarType width,
mitk::ScalarType height,
const AffineTransform3D *transform /* = nullptr */,
PlaneGeometry::PlaneOrientation planeorientation /* = Axial */,
mitk::ScalarType zPosition /* = 0 */,
bool frontside /* = true */,
bool rotated /* = false */,
bool top /* = true */)
{
Superclass::Initialize();
/// construct standard view.
// We define at the moment "frontside" as: axial from above,
// coronal from front (nose), saggital from right.
// TODO: Double check with medicals doctors or radiologists [ ].
// We define the orientation in patient's view, e.g. LAI is in a axial cut
// (parallel to the triangle ear-ear-nose):
// first axis: To the left ear of the patient
// seecond axis: To the nose of the patient
// third axis: To the legs of the patient.
// Options are: L/R left/right; A/P anterior/posterior; I/S inferior/superior
// (AKA caudal/cranial).
// We note on all cases in the following switch block r.h. for right handed
// or l.h. for left handed to describe the different cases.
// However, which system is chosen is defined at the end of the switch block.
// CAVE / be careful: the vectors right and bottom are relative to the plane
// and do NOT describe e.g. the right side of the patient.
Point3D origin;
/** Bottom means downwards, DV means Direction Vector. Both relative to the image! */
VnlVector rightDV(3), bottomDV(3);
/** Origin of this plane is by default a zero vector and implicitly in the top-left corner: */
origin.Fill(0);
/** This is different to all definitions in MITK, except the QT mouse clicks.
* But it is like this here and we don't want to change a running system.
* Just be aware, that IN THIS FUNCTION we define the origin at the top left (e.g. your screen). */
/** NormalDirection defines which axis (i.e. column index in the transform matrix)
* is perpendicular to the plane: */
int normalDirection;
switch (planeorientation) // Switch through our limited choice of standard planes:
{
case None:
/** Orientation 'None' shall be done like the axial plane orientation,
* for whatever reasons. */
case Axial:
if (frontside) // Radiologist's view from below. A cut along the triangle ear-ear-nose.
{
if (rotated == false)
/** Origin in the top-left corner, x=[1; 0; 0], y=[0; 1; 0], z=[0; 0; 1],
* origin=[0,0,zpos]: LAI (r.h.)
*
* 0---rightDV----> |
* | |
* | Picture of a finite, rectangular plane |
* | ( insert LOLCAT-scan here ^_^ ) |
* | |
* v _________________________________________|
*
*/
{
FillVector3D(origin, 0, 0, zPosition);
FillVector3D(rightDV, 1, 0, 0);
FillVector3D(bottomDV, 0, 1, 0);
}
else // Origin rotated to the bottom-right corner, x=[-1; 0; 0], y=[0; -1; 0], z=[0; 0; 1],
// origin=[w,h,zpos]: RPI (r.h.)
{ // Caveat emptor: Still using top-left as origin of index coordinate system!
FillVector3D(origin, width, height, zPosition);
FillVector3D(rightDV, -1, 0, 0);
FillVector3D(bottomDV, 0, -1, 0);
}
}
else // 'Backside, not frontside.' Neuro-Surgeons's view from above patient.
{
if (rotated == false) // x=[-1; 0; 0], y=[0; 1; 0], z=[0; 0; 1], origin=[w,0,zpos]: RAS (r.h.)
{
FillVector3D(origin, width, 0, zPosition);
FillVector3D(rightDV, -1, 0, 0);
FillVector3D(bottomDV, 0, 1, 0);
}
else // Origin in the bottom-left corner, x=[1; 0; 0], y=[0; -1; 0], z=[0; 0; 1],
// origin=[0,h,zpos]: LPS (r.h.)
{
FillVector3D(origin, 0, height, zPosition);
FillVector3D(rightDV, 1, 0, 0);
FillVector3D(bottomDV, 0, -1, 0);
}
}
normalDirection = 2; // That is S=Superior=z=third_axis=middlefinger in righthanded LPS-system.
break;
// Frontal is known as Coronal in mitk. Plane cuts through patient's ear-ear-heel-heel:
case Frontal:
if (frontside)
{
if (rotated == false) // x=[1; 0; 0], y=[0; 0; 1], z=[0; 1; 0], origin=[0,zpos,0]: LAI (r.h.)
{
FillVector3D(origin, 0, zPosition, 0);
FillVector3D(rightDV, 1, 0, 0);
FillVector3D(bottomDV, 0, 0, 1);
}
else // x=[-1;0;0], y=[0;0;-1], z=[0;1;0], origin=[w,zpos,h]: RAS (r.h.)
{
FillVector3D(origin, width, zPosition, height);
FillVector3D(rightDV, -1, 0, 0);
FillVector3D(bottomDV, 0, 0, -1);
}
}
else
{
if (rotated == false) // x=[-1;0;0], y=[0;0;1], z=[0;1;0], origin=[w,zpos,0]: RPI (r.h.)
{
FillVector3D(origin, width, zPosition, 0);
FillVector3D(rightDV, -1, 0, 0);
FillVector3D(bottomDV, 0, 0, 1);
}
else // x=[1;0;0], y=[0;1;0], z=[0;0;-1], origin=[0,zpos,h]: LPS (r.h.)
{
FillVector3D(origin, 0, zPosition, height);
FillVector3D(rightDV, 1, 0, 0);
FillVector3D(bottomDV, 0, 0, -1);
}
}
normalDirection = 1; // Normal vector = posterior direction.
break;
case Sagittal: // Sagittal=Medial plane, the symmetry-plane mirroring your face.
if (frontside)
{
if (rotated == false) // x=[0;1;0], y=[0;0;1], z=[1;0;0], origin=[zpos,0,0]: LAI (r.h.)
{
FillVector3D(origin, zPosition, 0, 0);
FillVector3D(rightDV, 0, 1, 0);
FillVector3D(bottomDV, 0, 0, 1);
}
else // x=[0;-1;0], y=[0;0;-1], z=[1;0;0], origin=[zpos,w,h]: LPS (r.h.)
{
FillVector3D(origin, zPosition, width, height);
FillVector3D(rightDV, 0, -1, 0);
FillVector3D(bottomDV, 0, 0, -1);
}
}
else
{
if (rotated == false) // x=[0;-1;0], y=[0;0;1], z=[1;0;0], origin=[zpos,w,0]: RPI (r.h.)
{
FillVector3D(origin, zPosition, width, 0);
FillVector3D(rightDV, 0, -1, 0);
FillVector3D(bottomDV, 0, 0, 1);
}
else // x=[0;1;0], y=[0;0;-1], z=[1;0;0], origin=[zpos,0,h]: RAS (r.h.)
{
FillVector3D(origin, zPosition, 0, height);
FillVector3D(rightDV, 0, 1, 0);
FillVector3D(bottomDV, 0, 0, -1);
}
}
normalDirection = 0; // Normal vector = Lateral direction: Left in a LPS-system.
break;
default:
itkExceptionMacro("unknown PlaneOrientation");
}
VnlVector normal(3);
FillVector3D(normal, 0, 0, 0);
normal[normalDirection] = top ? 1 : -1;
if ( transform != nullptr )
{
origin = transform->TransformPoint( origin );
- rightDV = transform->TransformVector( rightDV );
- bottomDV = transform->TransformVector( bottomDV );
- normal = transform->TransformVector( normal );
+ rightDV = transform->TransformVector( rightDV ).as_ref();
+ bottomDV = transform->TransformVector( bottomDV ).as_ref();
+ normal = transform->TransformVector( normal ).as_ref();
}
ScalarType bounds[6] = {0, width, 0, height, 0, 1};
this->SetBounds(bounds);
AffineTransform3D::Pointer planeTransform = AffineTransform3D::New();
Matrix3D matrix;
- matrix.GetVnlMatrix().set_column(0, rightDV);
- matrix.GetVnlMatrix().set_column(1, bottomDV);
- matrix.GetVnlMatrix().set_column(2, normal);
+ matrix.GetVnlMatrix().set_column(0, rightDV.as_ref());
+ matrix.GetVnlMatrix().set_column(1, bottomDV.as_ref());
+ matrix.GetVnlMatrix().set_column(2, normal.as_ref());
planeTransform->SetMatrix(matrix);
planeTransform->SetOffset(this->GetIndexToWorldTransform()->GetOffset());
this->SetIndexToWorldTransform(planeTransform);
this->SetOrigin(origin);
}
std::vector< int > PlaneGeometry::CalculateDominantAxes(mitk::AffineTransform3D::MatrixType::InternalMatrixType& rotation_matrix)
{
std::vector< int > axes;
bool dominant_axis_error = false;
for (int i = 0; i < 3; ++i)
{
int dominantAxis = itk::Function::Max3(
rotation_matrix[0][i],
rotation_matrix[1][i],
rotation_matrix[2][i]
);
for (int j=0; j<i; ++j)
if (axes[j] == dominantAxis)
{
dominant_axis_error = true;
break;
}
if (dominant_axis_error)
break;
axes.push_back(dominantAxis);
}
if (dominant_axis_error)
{
MITK_DEBUG << "Error during dominant axis calculation. Using default.";
MITK_DEBUG << "This is either caused by an imperfect rotation matrix or if the rotation is axactly 45° around one or more axis.";
axes.clear();
for (int i = 0; i < 3; ++i)
axes.push_back(i);
}
return axes;
}
void PlaneGeometry::InitializeStandardPlane(const BaseGeometry *geometry3D,
PlaneOrientation planeorientation,
ScalarType zPosition,
bool frontside,
bool rotated,
bool top)
{
this->SetReferenceGeometry(geometry3D);
ScalarType width, height;
// Inspired by:
// http://www.na-mic.org/Wiki/index.php/Coordinate_System_Conversion_Between_ITK_and_Slicer3
mitk::AffineTransform3D::MatrixType matrix = geometry3D->GetIndexToWorldTransform()->GetMatrix();
matrix.GetVnlMatrix().normalize_columns();
mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetTranspose();
/// The index of the sagittal, coronal and axial axes in the reference geometry.
auto axes = CalculateDominantAxes(inverseMatrix);
/// The direction of the sagittal, coronal and axial axes in the reference geometry.
/// +1 means that the direction is straight, i.e. greater index translates to greater
/// world coordinate. -1 means that the direction is inverted.
int directions[3];
ScalarType extents[3];
ScalarType spacings[3];
for (int i=0; i<3; ++i)
{
int dominantAxis = axes.at(i);
directions[i] = itk::Function::Sign(inverseMatrix[dominantAxis][i]);
extents[i] = geometry3D->GetExtent(dominantAxis);
spacings[i] = geometry3D->GetSpacing()[dominantAxis];
}
// matrix(column) = inverseTransformMatrix(row) * flippedAxes * spacing
matrix[0][0] = inverseMatrix[axes[0]][0] * directions[0] * spacings[0];
matrix[1][0] = inverseMatrix[axes[0]][1] * directions[0] * spacings[0];
matrix[2][0] = inverseMatrix[axes[0]][2] * directions[0] * spacings[0];
matrix[0][1] = inverseMatrix[axes[1]][0] * directions[1] * spacings[1];
matrix[1][1] = inverseMatrix[axes[1]][1] * directions[1] * spacings[1];
matrix[2][1] = inverseMatrix[axes[1]][2] * directions[1] * spacings[1];
matrix[0][2] = inverseMatrix[axes[2]][0] * directions[2] * spacings[2];
matrix[1][2] = inverseMatrix[axes[2]][1] * directions[2] * spacings[2];
matrix[2][2] = inverseMatrix[axes[2]][2] * directions[2] * spacings[2];
/// The "world origin" is the corner with the lowest physical coordinates.
/// We use it as a reference point so that we get the correct anatomical
/// orientations.
Point3D worldOrigin = geometry3D->GetOrigin();
for (int i = 0; i < 3; ++i)
{
/// The distance of the plane origin from the world origin in voxels.
double offset = directions[i] > 0 ? 0.0 : extents[i];
if (geometry3D->GetImageGeometry())
{
offset += directions[i] * 0.5;
}
for (int j = 0; j < 3; ++j)
{
worldOrigin[j] -= offset * matrix[j][i];
}
}
switch(planeorientation)
{
case None:
/** Orientation 'None' shall be done like the axial plane orientation,
* for whatever reasons. */
case Axial:
width = extents[0];
height = extents[1];
break;
case Frontal:
width = extents[0];
height = extents[2];
break;
case Sagittal:
width = extents[1];
height = extents[2];
break;
default:
itkExceptionMacro("unknown PlaneOrientation");
}
ScalarType bounds[6]= { 0, width, 0, height, 0, 1 };
this->SetBounds( bounds );
AffineTransform3D::Pointer transform = AffineTransform3D::New();
transform->SetMatrix(matrix);
transform->SetOffset(worldOrigin.GetVectorFromOrigin());
InitializeStandardPlane(
width, height, transform, planeorientation, zPosition, frontside, rotated, top);
}
void PlaneGeometry::InitializeStandardPlane(
const BaseGeometry *geometry3D, bool top, PlaneOrientation planeorientation, bool frontside, bool rotated)
{
/// The index of the sagittal, coronal and axial axes in world coordinate system.
int worldAxis;
switch(planeorientation)
{
case None:
/** Orientation 'None' shall be done like the axial plane orientation,
* for whatever reasons. */
case Axial:
worldAxis = 2;
break;
case Frontal:
worldAxis = 1;
break;
case Sagittal:
worldAxis = 0;
break;
default:
itkExceptionMacro("unknown PlaneOrientation");
}
// Inspired by:
// http://www.na-mic.org/Wiki/index.php/Coordinate_System_Conversion_Between_ITK_and_Slicer3
mitk::AffineTransform3D::ConstPointer affineTransform = geometry3D->GetIndexToWorldTransform();
mitk::AffineTransform3D::MatrixType matrix = affineTransform->GetMatrix();
matrix.GetVnlMatrix().normalize_columns();
mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse();
/// The index of the sagittal, coronal and axial axes in the reference geometry.
int dominantAxis = CalculateDominantAxes(inverseMatrix).at(worldAxis);
ScalarType zPosition = top ? 0.5 : geometry3D->GetExtent(dominantAxis) - 0.5;
InitializeStandardPlane(geometry3D, planeorientation, zPosition, frontside, rotated, top);
}
void PlaneGeometry::InitializeStandardPlane(const Vector3D &rightVector,
const Vector3D &downVector,
const Vector3D *spacing)
{
InitializeStandardPlane(rightVector.GetVnlVector(), downVector.GetVnlVector(), spacing);
}
void PlaneGeometry::InitializeStandardPlane(const VnlVector &rightVector,
const VnlVector &downVector,
const Vector3D *spacing)
{
ScalarType width = rightVector.two_norm();
ScalarType height = downVector.two_norm();
InitializeStandardPlane(width, height, rightVector, downVector, spacing);
}
void PlaneGeometry::InitializeStandardPlane(mitk::ScalarType width,
ScalarType height,
const Vector3D &rightVector,
const Vector3D &downVector,
const Vector3D *spacing)
{
InitializeStandardPlane(width, height, rightVector.GetVnlVector(), downVector.GetVnlVector(), spacing);
}
void PlaneGeometry::InitializeStandardPlane(mitk::ScalarType width,
ScalarType height,
const VnlVector &rightVector,
const VnlVector &downVector,
const Vector3D *spacing)
{
assert(width > 0);
assert(height > 0);
VnlVector rightDV = rightVector;
rightDV.normalize();
VnlVector downDV = downVector;
downDV.normalize();
VnlVector normal = vnl_cross_3d(rightVector, downVector);
normal.normalize();
// Crossproduct vnl_cross_3d is always righthanded, but that is okay here
// because in this method we create a new IndexToWorldTransform and
// spacing with 1 or 3 negative components could still make it lefthanded.
if (spacing != nullptr)
{
rightDV *= (*spacing)[0];
downDV *= (*spacing)[1];
normal *= (*spacing)[2];
}
AffineTransform3D::Pointer transform = AffineTransform3D::New();
Matrix3D matrix;
matrix.GetVnlMatrix().set_column(0, rightDV);
matrix.GetVnlMatrix().set_column(1, downDV);
matrix.GetVnlMatrix().set_column(2, normal);
transform->SetMatrix(matrix);
transform->SetOffset(this->GetIndexToWorldTransform()->GetOffset());
ScalarType bounds[6] = {0, width, 0, height, 0, 1};
this->SetBounds(bounds);
this->SetIndexToWorldTransform(transform);
}
void PlaneGeometry::InitializePlane(const Point3D &origin, const Vector3D &normal)
{
VnlVector rightVectorVnl(3), downVectorVnl;
if (Equal(normal[1], 0.0f) == false)
{
FillVector3D(rightVectorVnl, 1.0f, -normal[0] / normal[1], 0.0f);
rightVectorVnl.normalize();
}
else
{
FillVector3D(rightVectorVnl, 0.0f, 1.0f, 0.0f);
}
downVectorVnl = vnl_cross_3d(normal.GetVnlVector(), rightVectorVnl);
downVectorVnl.normalize();
// Crossproduct vnl_cross_3d is always righthanded.
InitializeStandardPlane(rightVectorVnl, downVectorVnl);
SetOrigin(origin);
}
void PlaneGeometry::SetMatrixByVectors(const VnlVector &rightVector,
const VnlVector &downVector,
ScalarType thickness /* = 1.0 */)
{
VnlVector normal = vnl_cross_3d(rightVector, downVector);
normal.normalize();
normal *= thickness;
// Crossproduct vnl_cross_3d is always righthanded, but that is okay here
// because in this method we create a new IndexToWorldTransform and
// a negative thickness could still make it lefthanded.
AffineTransform3D::Pointer transform = AffineTransform3D::New();
Matrix3D matrix;
matrix.GetVnlMatrix().set_column(0, rightVector);
matrix.GetVnlMatrix().set_column(1, downVector);
matrix.GetVnlMatrix().set_column(2, normal);
transform->SetMatrix(matrix);
transform->SetOffset(this->GetIndexToWorldTransform()->GetOffset());
SetIndexToWorldTransform(transform);
}
Vector3D PlaneGeometry::GetNormal() const
{
Vector3D frontToBack;
- frontToBack.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2));
+ frontToBack.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref());
return frontToBack;
}
VnlVector PlaneGeometry::GetNormalVnl() const
{
- return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2);
+ return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref();
}
ScalarType PlaneGeometry::DistanceFromPlane(const Point3D &pt3d_mm) const { return fabs(SignedDistance(pt3d_mm)); }
ScalarType PlaneGeometry::SignedDistance(const Point3D &pt3d_mm) const { return SignedDistanceFromPlane(pt3d_mm); }
bool PlaneGeometry::IsAbove(const Point3D &pt3d_mm, bool considerBoundingBox) const
{
if (considerBoundingBox)
{
Point3D pt3d_units;
BaseGeometry::WorldToIndex(pt3d_mm, pt3d_units);
return (pt3d_units[2] > this->GetBoundingBox()->GetBounds()[4]);
}
else
return SignedDistanceFromPlane(pt3d_mm) > 0;
}
- bool PlaneGeometry::IntersectionLine(const PlaneGeometry *plane, Line3D &crossline) const
+ bool PlaneGeometry::IntersectionLine(const PlaneGeometry* plane, Line3D& crossline) const
{
Vector3D normal = this->GetNormal();
normal.Normalize();
Vector3D planeNormal = plane->GetNormal();
planeNormal.Normalize();
Vector3D direction = itk::CrossProduct(normal, planeNormal);
if (direction.GetSquaredNorm() < eps)
return false;
crossline.SetDirection(direction);
double N1dN2 = normal * planeNormal;
double determinant = 1.0 - N1dN2 * N1dN2;
Vector3D origin = this->GetOrigin().GetVectorFromOrigin();
Vector3D planeOrigin = plane->GetOrigin().GetVectorFromOrigin();
double d1 = normal * origin;
double d2 = planeNormal * planeOrigin;
double c1 = (d1 - d2 * N1dN2) / determinant;
double c2 = (d2 - d1 * N1dN2) / determinant;
Vector3D p = normal * c1 + planeNormal * c2;
- crossline.GetPoint().GetVnlVector() = p.GetVnlVector();
+ crossline.GetPoint()[0] = p.GetVnlVector()[0];
+ crossline.GetPoint()[1] = p.GetVnlVector()[1];
+ crossline.GetPoint()[2] = p.GetVnlVector()[2];
return true;
}
unsigned int PlaneGeometry::IntersectWithPlane2D(const PlaneGeometry *plane, Point2D &lineFrom, Point2D &lineTo) const
{
Line3D crossline;
if (this->IntersectionLine(plane, crossline) == false)
return 0;
Point2D point2;
Vector2D direction2;
this->Map(crossline.GetPoint(), point2);
this->Map(crossline.GetPoint(), crossline.GetDirection(), direction2);
return Line3D::RectangleLineIntersection(
0, 0, GetExtentInMM(0), GetExtentInMM(1), point2, direction2, lineFrom, lineTo);
}
double PlaneGeometry::Angle(const PlaneGeometry *plane) const
{
return angle(plane->GetMatrixColumn(2), GetMatrixColumn(2));
}
double PlaneGeometry::Angle(const Line3D &line) const
{
return vnl_math::pi_over_2 - angle(line.GetDirection().GetVnlVector(), GetMatrixColumn(2));
}
bool PlaneGeometry::IntersectionPoint(const Line3D &line, Point3D &intersectionPoint) const
{
Vector3D planeNormal = this->GetNormal();
planeNormal.Normalize();
Vector3D lineDirection = line.GetDirection();
lineDirection.Normalize();
double t = planeNormal * lineDirection;
if (fabs(t) < eps)
{
return false;
}
Vector3D diff;
diff = this->GetOrigin() - line.GetPoint();
t = (planeNormal * diff) / t;
intersectionPoint = line.GetPoint() + lineDirection * t;
return true;
}
bool PlaneGeometry::IntersectionPointParam(const Line3D &line, double &t) const
{
Vector3D planeNormal = this->GetNormal();
Vector3D lineDirection = line.GetDirection();
t = planeNormal * lineDirection;
if (fabs(t) < eps)
{
return false;
}
Vector3D diff;
diff = this->GetOrigin() - line.GetPoint();
t = (planeNormal * diff) / t;
return true;
}
bool PlaneGeometry::IsParallel(const PlaneGeometry *plane) const
{
return ((Angle(plane) < 10.0 * mitk::sqrteps) || (Angle(plane) > (vnl_math::pi - 10.0 * sqrteps)));
}
bool PlaneGeometry::IsOnPlane(const Point3D &point) const { return Distance(point) < eps; }
bool PlaneGeometry::IsOnPlane(const Line3D &line) const
{
return ((Distance(line.GetPoint()) < eps) && (Distance(line.GetPoint2()) < eps));
}
bool PlaneGeometry::IsOnPlane(const PlaneGeometry *plane) const
{
return (IsParallel(plane) && (Distance(plane->GetOrigin()) < eps));
}
Point3D PlaneGeometry::ProjectPointOntoPlane(const Point3D &pt) const
{
ScalarType len = this->GetNormalVnl().two_norm();
return pt - this->GetNormal() * this->SignedDistanceFromPlane(pt) / len;
}
itk::LightObject::Pointer PlaneGeometry::InternalClone() const
{
Self::Pointer newGeometry = new PlaneGeometry(*this);
newGeometry->UnRegister();
return newGeometry.GetPointer();
}
void PlaneGeometry::ExecuteOperation(Operation *operation)
{
vtkTransform *transform = vtkTransform::New();
transform->SetMatrix(this->GetVtkMatrix());
switch (operation->GetOperationType())
{
case OpORIENT:
{
auto *planeOp = dynamic_cast<mitk::PlaneOperation *>(operation);
if (planeOp == nullptr)
{
return;
}
Point3D center = planeOp->GetPoint();
Vector3D orientationVector = planeOp->GetNormal();
Vector3D defaultVector;
FillVector3D(defaultVector, 0.0, 0.0, 1.0);
Vector3D rotationAxis = itk::CrossProduct(orientationVector, defaultVector);
// double rotationAngle = acos( orientationVector[2] / orientationVector.GetNorm() );
double rotationAngle = atan2((double)rotationAxis.GetNorm(), (double)(orientationVector * defaultVector));
rotationAngle *= 180.0 / vnl_math::pi;
transform->PostMultiply();
transform->Identity();
transform->Translate(center[0], center[1], center[2]);
transform->RotateWXYZ(rotationAngle, rotationAxis[0], rotationAxis[1], rotationAxis[2]);
transform->Translate(-center[0], -center[1], -center[2]);
break;
}
case OpRESTOREPLANEPOSITION:
{
auto *op = dynamic_cast<mitk::RestorePlanePositionOperation *>(operation);
if (op == nullptr)
{
return;
}
AffineTransform3D::Pointer transform2 = AffineTransform3D::New();
Matrix3D matrix;
matrix.GetVnlMatrix().set_column(0, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(0));
matrix.GetVnlMatrix().set_column(1, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(1));
matrix.GetVnlMatrix().set_column(2, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(2));
transform2->SetMatrix(matrix);
Vector3D offset = op->GetTransform()->GetOffset();
transform2->SetOffset(offset);
this->SetIndexToWorldTransform(transform2);
ScalarType bounds[6] = {0, op->GetWidth(), 0, op->GetHeight(), 0, 1};
this->SetBounds(bounds);
this->Modified();
transform->Delete();
return;
}
default:
Superclass::ExecuteOperation(operation);
transform->Delete();
return;
}
this->SetVtkMatrixDeepCopy(transform);
this->Modified();
transform->Delete();
}
void PlaneGeometry::PrintSelf(std::ostream &os, itk::Indent indent) const
{
Superclass::PrintSelf(os, indent);
os << indent << " ScaleFactorMMPerUnitX: " << GetExtentInMM(0) / GetExtent(0) << std::endl;
os << indent << " ScaleFactorMMPerUnitY: " << GetExtentInMM(1) / GetExtent(1) << std::endl;
os << indent << " Normal: " << GetNormal() << std::endl;
}
bool PlaneGeometry::Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const
{
assert(this->IsBoundingBoxNull() == false);
Point3D pt3d_units;
Superclass::WorldToIndex(pt3d_mm, pt3d_units);
pt2d_mm[0] = pt3d_units[0] * GetExtentInMM(0) / GetExtent(0);
pt2d_mm[1] = pt3d_units[1] * GetExtentInMM(1) / GetExtent(1);
pt3d_units[2] = 0;
return this->GetBoundingBox()->IsInside(pt3d_units);
}
void PlaneGeometry::Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const
{
// pt2d_mm is measured from the origin of the world geometry (at leats it called form BaseRendere::Mouse...Event)
Point3D pt3d_units;
pt3d_units[0] = pt2d_mm[0] / (GetExtentInMM(0) / GetExtent(0));
pt3d_units[1] = pt2d_mm[1] / (GetExtentInMM(1) / GetExtent(1));
pt3d_units[2] = 0;
// pt3d_units is a continuos index. We divided it with the Scale Factor (= spacing in x and y) to convert it from mm
// to index units.
//
pt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units);
// now we convert the 3d index to a 3D world point in mm. We could have used IndexToWorld as well as
// GetITW->Transform...
}
void PlaneGeometry::SetSizeInUnits(mitk::ScalarType width, mitk::ScalarType height)
{
ScalarType bounds[6] = {0, width, 0, height, 0, 1};
ScalarType extent, newextentInMM;
if (GetExtent(0) > 0)
{
extent = GetExtent(0);
if (width > extent)
newextentInMM = GetExtentInMM(0) / width * extent;
else
newextentInMM = GetExtentInMM(0) * extent / width;
SetExtentInMM(0, newextentInMM);
}
if (GetExtent(1) > 0)
{
extent = GetExtent(1);
if (width > extent)
newextentInMM = GetExtentInMM(1) / height * extent;
else
newextentInMM = GetExtentInMM(1) * extent / height;
SetExtentInMM(1, newextentInMM);
}
SetBounds(bounds);
}
bool PlaneGeometry::Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const
{
assert(this->IsBoundingBoxNull() == false);
Point3D pt3d_units;
Superclass::WorldToIndex(pt3d_mm, pt3d_units);
pt3d_units[2] = 0;
projectedPt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units);
return this->GetBoundingBox()->IsInside(pt3d_units);
}
bool PlaneGeometry::Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const
{
assert(this->IsBoundingBoxNull() == false);
Vector3D vec3d_units;
Superclass::WorldToIndex(vec3d_mm, vec3d_units);
vec3d_units[2] = 0;
projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units);
return true;
}
bool PlaneGeometry::Project(const mitk::Point3D &atPt3d_mm,
const mitk::Vector3D &vec3d_mm,
mitk::Vector3D &projectedVec3d_mm) const
{
MITK_WARN << "Deprecated function! Call Project(vec3D,vec3D) instead.";
assert(this->IsBoundingBoxNull() == false);
Vector3D vec3d_units;
Superclass::WorldToIndex(atPt3d_mm, vec3d_mm, vec3d_units);
vec3d_units[2] = 0;
projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units);
Point3D pt3d_units;
Superclass::WorldToIndex(atPt3d_mm, pt3d_units);
return this->GetBoundingBox()->IsInside(pt3d_units);
}
bool PlaneGeometry::Map(const mitk::Point3D &atPt3d_mm,
const mitk::Vector3D &vec3d_mm,
mitk::Vector2D &vec2d_mm) const
{
Point2D pt2d_mm_start, pt2d_mm_end;
Point3D pt3d_mm_end;
bool inside = Map(atPt3d_mm, pt2d_mm_start);
pt3d_mm_end = atPt3d_mm + vec3d_mm;
inside &= Map(pt3d_mm_end, pt2d_mm_end);
vec2d_mm = pt2d_mm_end - pt2d_mm_start;
return inside;
}
void PlaneGeometry::Map(const mitk::Point2D & /*atPt2d_mm*/,
const mitk::Vector2D & /*vec2d_mm*/,
mitk::Vector3D & /*vec3d_mm*/) const
{
//@todo implement parallel to the other Map method!
assert(false);
}
void PlaneGeometry::SetReferenceGeometry(const mitk::BaseGeometry *geometry) { m_ReferenceGeometry = geometry; }
const mitk::BaseGeometry *PlaneGeometry::GetReferenceGeometry() const { return m_ReferenceGeometry; }
bool PlaneGeometry::HasReferenceGeometry() const { return (m_ReferenceGeometry != nullptr); }
} // namespace
diff --git a/Modules/Core/src/DataManagement/mitkPointSet.cpp b/Modules/Core/src/DataManagement/mitkPointSet.cpp
index e8294bb02b..024ad92dec 100755
--- a/Modules/Core/src/DataManagement/mitkPointSet.cpp
+++ b/Modules/Core/src/DataManagement/mitkPointSet.cpp
@@ -1,955 +1,965 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkPointSet.h"
#include "mitkInteractionConst.h"
#include "mitkPointOperation.h"
#include <iomanip>
#include <mitkNumericTypes.h>
+namespace mitk
+{
+ itkEventMacroDefinition(PointSetEvent, itk::AnyEvent);
+ itkEventMacroDefinition(PointSetMoveEvent, PointSetEvent);
+ itkEventMacroDefinition(PointSetSizeChangeEvent, PointSetEvent);
+ itkEventMacroDefinition(PointSetAddEvent, PointSetSizeChangeEvent);
+ itkEventMacroDefinition(PointSetRemoveEvent, PointSetSizeChangeEvent);
+ itkEventMacroDefinition(PointSetExtendTimeRangeEvent, PointSetEvent);
+}
+
mitk::PointSet::PointSet() : m_CalculateBoundingBox(true)
{
this->InitializeEmpty();
}
mitk::PointSet::PointSet(const PointSet &other)
: BaseData(other), m_PointSetSeries(other.GetPointSetSeriesSize()), m_CalculateBoundingBox(true)
{
// Copy points
for (std::size_t t = 0; t < m_PointSetSeries.size(); ++t)
{
m_PointSetSeries[t] = DataType::New();
DataType::Pointer otherPts = other.GetPointSet(t);
for (PointsConstIterator i = other.Begin(t); i != other.End(t); ++i)
{
m_PointSetSeries[t]->SetPoint(i.Index(), i.Value());
PointDataType pointData;
if (otherPts->GetPointData(i.Index(), &pointData))
{
m_PointSetSeries[t]->SetPointData(i.Index(), pointData);
}
}
}
}
mitk::PointSet::~PointSet()
{
this->ClearData();
}
void mitk::PointSet::ClearData()
{
m_PointSetSeries.clear();
Superclass::ClearData();
}
void mitk::PointSet::InitializeEmpty()
{
m_PointSetSeries.resize(1);
m_PointSetSeries[0] = DataType::New();
PointDataContainer::Pointer pointData = PointDataContainer::New();
m_PointSetSeries[0]->SetPointData(pointData);
m_CalculateBoundingBox = false;
Superclass::InitializeTimeGeometry(1);
m_Initialized = true;
m_EmptyPointsContainer = DataType::PointsContainer::New();
}
bool mitk::PointSet::IsEmptyTimeStep(unsigned int t) const
{
return IsInitialized() && (GetSize(t) == 0);
}
void mitk::PointSet::Expand(unsigned int timeSteps)
{
// Check if the vector is long enough to contain the new element
// at the given position. If not, expand it with sufficient pre-initialized
// elements.
//
// NOTE: This method will never REDUCE the vector size; it should only
// be used to make sure that the vector has enough elements to include the
// specified time step.
unsigned int oldSize = m_PointSetSeries.size();
if (timeSteps > oldSize)
{
Superclass::Expand(timeSteps);
m_PointSetSeries.resize(timeSteps);
for (unsigned int i = oldSize; i < timeSteps; ++i)
{
m_PointSetSeries[i] = DataType::New();
PointDataContainer::Pointer pointData = PointDataContainer::New();
m_PointSetSeries[i]->SetPointData(pointData);
}
// if the size changes, then compute the bounding box
m_CalculateBoundingBox = true;
this->InvokeEvent(PointSetExtendTimeRangeEvent());
}
}
unsigned int mitk::PointSet::GetPointSetSeriesSize() const
{
return m_PointSetSeries.size();
}
int mitk::PointSet::GetSize(unsigned int t) const
{
if (t < m_PointSetSeries.size())
{
return m_PointSetSeries[t]->GetNumberOfPoints();
}
else
{
return 0;
}
}
mitk::PointSet::DataType::Pointer mitk::PointSet::GetPointSet(int t) const
{
if (t < (int)m_PointSetSeries.size())
{
return m_PointSetSeries[t];
}
else
{
return nullptr;
}
}
mitk::PointSet::PointsIterator mitk::PointSet::Begin(int t)
{
if (t >= 0 && t < static_cast<int>(m_PointSetSeries.size()))
{
return m_PointSetSeries[t]->GetPoints()->Begin();
}
return m_EmptyPointsContainer->End();
}
mitk::PointSet::PointsConstIterator mitk::PointSet::Begin(int t) const
{
if (t >= 0 && t < static_cast<int>(m_PointSetSeries.size()))
{
return m_PointSetSeries[t]->GetPoints()->Begin();
}
return m_EmptyPointsContainer->End();
}
mitk::PointSet::PointsIterator mitk::PointSet::End(int t)
{
if (t >= 0 && t < static_cast<int>(m_PointSetSeries.size()))
{
return m_PointSetSeries[t]->GetPoints()->End();
}
return m_EmptyPointsContainer->End();
}
mitk::PointSet::PointsConstIterator mitk::PointSet::End(int t) const
{
if (t >= 0 && t < static_cast<int>(m_PointSetSeries.size()))
{
return m_PointSetSeries[t]->GetPoints()->End();
}
return m_EmptyPointsContainer->End();
}
mitk::PointSet::PointsIterator mitk::PointSet::GetMaxId(int t)
{
if ((unsigned int)t >= m_PointSetSeries.size())
{
return m_EmptyPointsContainer->End();
}
return this->Begin(t) == this->End(t) ? this->End(t) : --End(t);
}
int mitk::PointSet::SearchPoint(Point3D point, ScalarType distance, int t) const
{
if (t >= (int)m_PointSetSeries.size())
{
return -1;
}
// Out is the point which is checked to be the searched point
PointType out;
out.Fill(0);
PointType indexPoint;
this->GetGeometry(t)->WorldToIndex(point, indexPoint);
// Searching the first point in the Set, that is +- distance far away fro
// the given point
unsigned int i;
PointsContainer::Iterator it, end;
end = m_PointSetSeries[t]->GetPoints()->End();
int bestIndex = -1;
distance = distance * distance;
// To correct errors from converting index to world and world to index
if (distance == 0.0)
{
distance = 0.000001;
}
ScalarType bestDist = distance;
ScalarType dist, tmp;
for (it = m_PointSetSeries[t]->GetPoints()->Begin(), i = 0; it != end; ++it, ++i)
{
bool ok = m_PointSetSeries[t]->GetPoints()->GetElementIfIndexExists(it->Index(), &out);
if (!ok)
{
return -1;
}
else if (indexPoint == out) // if totally equal
{
return it->Index();
}
// distance calculation
tmp = out[0] - indexPoint[0];
dist = tmp * tmp;
tmp = out[1] - indexPoint[1];
dist += tmp * tmp;
tmp = out[2] - indexPoint[2];
dist += tmp * tmp;
if (dist < bestDist)
{
bestIndex = it->Index();
bestDist = dist;
}
}
return bestIndex;
}
mitk::PointSet::PointType mitk::PointSet::GetPoint(PointIdentifier id, int t) const
{
PointType out;
out.Fill(0);
if ((unsigned int)t >= m_PointSetSeries.size())
{
return out;
}
if (m_PointSetSeries[t]->GetPoints()->IndexExists(id))
{
m_PointSetSeries[t]->GetPoint(id, &out);
this->GetGeometry(t)->IndexToWorld(out, out);
return out;
}
else
{
return out;
}
}
bool mitk::PointSet::GetPointIfExists(PointIdentifier id, PointType *point, int t) const
{
if ((unsigned int)t >= m_PointSetSeries.size())
{
return false;
}
if (m_PointSetSeries[t]->GetPoints()->GetElementIfIndexExists(id, point))
{
this->GetGeometry(t)->IndexToWorld(*point, *point);
return true;
}
else
{
return false;
}
}
void mitk::PointSet::SetPoint(PointIdentifier id, PointType point, int t)
{
// Adapt the size of the data vector if necessary
this->Expand(t + 1);
mitk::Point3D indexPoint;
this->GetGeometry(t)->WorldToIndex(point, indexPoint);
m_PointSetSeries[t]->SetPoint(id, indexPoint);
PointDataType defaultPointData;
defaultPointData.id = id;
defaultPointData.selected = false;
defaultPointData.pointSpec = mitk::PTUNDEFINED;
m_PointSetSeries[t]->SetPointData(id, defaultPointData);
// boundingbox has to be computed anyway
m_CalculateBoundingBox = true;
this->Modified();
}
void mitk::PointSet::SetPoint(PointIdentifier id, PointType point, PointSpecificationType spec, int t)
{
// Adapt the size of the data vector if necessary
this->Expand(t + 1);
mitk::Point3D indexPoint;
this->GetGeometry(t)->WorldToIndex(point, indexPoint);
m_PointSetSeries[t]->SetPoint(id, indexPoint);
PointDataType defaultPointData;
defaultPointData.id = id;
defaultPointData.selected = false;
defaultPointData.pointSpec = spec;
m_PointSetSeries[t]->SetPointData(id, defaultPointData);
// boundingbox has to be computed anyway
m_CalculateBoundingBox = true;
this->Modified();
}
void mitk::PointSet::InsertPoint(PointIdentifier id, PointType point, int t)
{
this->InsertPoint(id, point, mitk::PTUNDEFINED, t);
}
void mitk::PointSet::InsertPoint(PointIdentifier id, PointType point, PointSpecificationType spec, int t)
{
if ((unsigned int)t < m_PointSetSeries.size())
{
mitk::Point3D indexPoint;
mitk::BaseGeometry *tempGeometry = this->GetGeometry(t);
if (tempGeometry == nullptr)
{
MITK_INFO << __FILE__ << ", l." << __LINE__ << ": GetGeometry of " << t << " returned nullptr!" << std::endl;
return;
}
tempGeometry->WorldToIndex(point, indexPoint);
m_PointSetSeries[t]->GetPoints()->InsertElement(id, indexPoint);
PointDataType defaultPointData;
defaultPointData.id = id;
defaultPointData.selected = false;
defaultPointData.pointSpec = spec;
m_PointSetSeries[t]->GetPointData()->InsertElement(id, defaultPointData);
// boundingbox has to be computed anyway
m_CalculateBoundingBox = true;
this->Modified();
}
}
mitk::PointSet::PointIdentifier mitk::PointSet::InsertPoint(PointType point, int t)
{
// Adapt the size of the data vector if necessary
this->Expand(t + 1);
PointIdentifier id = 0;
if (m_PointSetSeries[t]->GetNumberOfPoints() > 0)
{
PointsIterator it = --End(t);
id = it.Index();
++id;
}
mitk::Point3D indexPoint;
this->GetGeometry(t)->WorldToIndex(point, indexPoint);
m_PointSetSeries[t]->SetPoint(id, indexPoint);
PointDataType defaultPointData;
defaultPointData.id = id;
defaultPointData.selected = false;
defaultPointData.pointSpec = mitk::PTUNDEFINED;
m_PointSetSeries[t]->SetPointData(id, defaultPointData);
// boundingbox has to be computed anyway
m_CalculateBoundingBox = true;
this->Modified();
return id;
}
bool mitk::PointSet::RemovePointIfExists(PointIdentifier id, int t)
{
if ((unsigned int)t < m_PointSetSeries.size())
{
DataType *pointSet = m_PointSetSeries[t];
PointsContainer *points = pointSet->GetPoints();
PointDataContainer *pdata = pointSet->GetPointData();
bool exists = points->IndexExists(id);
if (exists)
{
points->DeleteIndex(id);
pdata->DeleteIndex(id);
return true;
}
}
return false;
}
mitk::PointSet::PointsIterator mitk::PointSet::RemovePointAtEnd(int t)
{
if ((unsigned int)t < m_PointSetSeries.size())
{
DataType *pointSet = m_PointSetSeries[t];
PointsContainer *points = pointSet->GetPoints();
PointDataContainer *pdata = pointSet->GetPointData();
PointsIterator bit = points->Begin();
PointsIterator eit = points->End();
if (eit != bit)
{
PointsContainer::ElementIdentifier id = (--eit).Index();
points->DeleteIndex(id);
pdata->DeleteIndex(id);
PointsIterator eit2 = points->End();
return --eit2;
}
else
{
return eit;
}
}
return m_EmptyPointsContainer->End();
}
bool mitk::PointSet::SwapPointPosition(PointIdentifier id, bool moveUpwards, int t)
{
if (IndexExists(id, t))
{
PointType point = GetPoint(id, t);
if (moveUpwards)
{ // up
if (IndexExists(id - 1, t))
{
InsertPoint(id, GetPoint(id - 1, t), t);
InsertPoint(id - 1, point, t);
this->Modified();
return true;
}
}
else
{ // down
if (IndexExists(id + 1, t))
{
InsertPoint(id, GetPoint(id + 1, t), t);
InsertPoint(id + 1, point, t);
this->Modified();
return true;
}
}
}
return false;
}
bool mitk::PointSet::IndexExists(int position, int t) const
{
if ((unsigned int)t < m_PointSetSeries.size())
{
return m_PointSetSeries[t]->GetPoints()->IndexExists(position);
}
else
{
return false;
}
}
bool mitk::PointSet::GetSelectInfo(int position, int t) const
{
if (this->IndexExists(position, t))
{
PointDataType pointData = {0, false, PTUNDEFINED};
m_PointSetSeries[t]->GetPointData(position, &pointData);
return pointData.selected;
}
else
{
return false;
}
}
void mitk::PointSet::SetSelectInfo(int position, bool selected, int t)
{
if (this->IndexExists(position, t))
{
// timeStep to ms
TimePointType timeInMS = this->GetTimeGeometry()->TimeStepToTimePoint(t);
// point
Point3D point = this->GetPoint(position, t);
std::unique_ptr<PointOperation> op;
if (selected)
{
op.reset(new mitk::PointOperation(OpSELECTPOINT, timeInMS, point, position));
}
else
{
op.reset(new mitk::PointOperation(OpDESELECTPOINT, timeInMS, point, position));
}
this->ExecuteOperation(op.get());
}
}
mitk::PointSpecificationType mitk::PointSet::GetSpecificationTypeInfo(int position, int t) const
{
if (this->IndexExists(position, t))
{
PointDataType pointData = {0, false, PTUNDEFINED};
m_PointSetSeries[t]->GetPointData(position, &pointData);
return pointData.pointSpec;
}
else
{
return PTUNDEFINED;
}
}
int mitk::PointSet::GetNumberOfSelected(int t) const
{
if ((unsigned int)t >= m_PointSetSeries.size())
{
return 0;
}
int numberOfSelected = 0;
PointDataIterator it;
for (it = m_PointSetSeries[t]->GetPointData()->Begin(); it != m_PointSetSeries[t]->GetPointData()->End(); it++)
{
if (it->Value().selected == true)
{
++numberOfSelected;
}
}
return numberOfSelected;
}
int mitk::PointSet::SearchSelectedPoint(int t) const
{
if ((unsigned int)t >= m_PointSetSeries.size())
{
return -1;
}
PointDataIterator it;
for (it = m_PointSetSeries[t]->GetPointData()->Begin(); it != m_PointSetSeries[t]->GetPointData()->End(); it++)
{
if (it->Value().selected == true)
{
return it->Index();
}
}
return -1;
}
void mitk::PointSet::ExecuteOperation(Operation *operation)
{
int timeStep = -1;
mitkCheckOperationTypeMacro(PointOperation, operation, pointOp);
if (pointOp)
{
timeStep = this->GetTimeGeometry()->TimePointToTimeStep(pointOp->GetTimeInMS());
}
if (timeStep < 0)
{
MITK_ERROR << "Time step (" << timeStep << ") outside of PointSet time bounds" << std::endl;
return;
}
switch (operation->GetOperationType())
{
case OpNOTHING:
break;
case OpINSERT: // inserts the point at the given position and selects it.
{
int position = pointOp->GetIndex();
PointType pt;
pt.CastFrom(pointOp->GetPoint());
if (timeStep >= (int)this->GetTimeSteps())
this->Expand(timeStep + 1);
// transfer from world to index coordinates
mitk::BaseGeometry *geometry = this->GetGeometry(timeStep);
if (geometry == nullptr)
{
MITK_INFO << "GetGeometry returned nullptr!\n";
return;
}
geometry->WorldToIndex(pt, pt);
m_PointSetSeries[timeStep]->GetPoints()->InsertElement(position, pt);
PointDataType pointData = {
static_cast<unsigned int>(pointOp->GetIndex()), pointOp->GetSelected(), pointOp->GetPointType()};
m_PointSetSeries[timeStep]->GetPointData()->InsertElement(position, pointData);
this->Modified();
// boundingbox has to be computed
m_CalculateBoundingBox = true;
this->InvokeEvent(PointSetAddEvent());
this->OnPointSetChange();
}
break;
case OpMOVE: // moves the point given by index
{
PointType pt;
pt.CastFrom(pointOp->GetPoint());
// transfer from world to index coordinates
this->GetGeometry(timeStep)->WorldToIndex(pt, pt);
// Copy new point into container
m_PointSetSeries[timeStep]->SetPoint(pointOp->GetIndex(), pt);
// Insert a default point data object to keep the containers in sync
// (if no point data object exists yet)
PointDataType pointData;
if (!m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData))
{
m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData);
}
this->OnPointSetChange();
this->Modified();
// boundingbox has to be computed anyway
m_CalculateBoundingBox = true;
this->InvokeEvent(PointSetMoveEvent());
}
break;
case OpREMOVE: // removes the point at given by position
{
m_PointSetSeries[timeStep]->GetPoints()->DeleteIndex((unsigned)pointOp->GetIndex());
m_PointSetSeries[timeStep]->GetPointData()->DeleteIndex((unsigned)pointOp->GetIndex());
this->OnPointSetChange();
this->Modified();
// boundingbox has to be computed anyway
m_CalculateBoundingBox = true;
this->InvokeEvent(PointSetRemoveEvent());
}
break;
case OpSELECTPOINT: // select the given point
{
PointDataType pointData = {0, false, PTUNDEFINED};
m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData);
pointData.selected = true;
m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData);
this->Modified();
}
break;
case OpDESELECTPOINT: // unselect the given point
{
PointDataType pointData = {0, false, PTUNDEFINED};
m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData);
pointData.selected = false;
m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData);
this->Modified();
}
break;
case OpSETPOINTTYPE:
{
PointDataType pointData = {0, false, PTUNDEFINED};
m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData);
pointData.pointSpec = pointOp->GetPointType();
m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData);
this->Modified();
}
break;
case OpMOVEPOINTUP: // swap content of point with ID pointOp->GetIndex() with the point preceding it in the
// container // move point position within the pointset
{
PointIdentifier currentID = pointOp->GetIndex();
/* search for point with this id and point that precedes this one in the data container */
PointsContainer::STLContainerType points = m_PointSetSeries[timeStep]->GetPoints()->CastToSTLContainer();
auto it = points.find(currentID);
if (it == points.end()) // ID not found
break;
if (it == points.begin()) // we are at the first element, there is no previous element
break;
/* get and cache current point & pointdata and previous point & pointdata */
--it;
PointIdentifier prevID = it->first;
if (this->SwapPointContents(prevID, currentID, timeStep) == true)
this->Modified();
}
break;
case OpMOVEPOINTDOWN: // move point position within the pointset
{
PointIdentifier currentID = pointOp->GetIndex();
/* search for point with this id and point that succeeds this one in the data container */
PointsContainer::STLContainerType points = m_PointSetSeries[timeStep]->GetPoints()->CastToSTLContainer();
auto it = points.find(currentID);
if (it == points.end()) // ID not found
break;
++it;
if (it == points.end()) // ID is already the last element, there is no succeeding element
break;
/* get and cache current point & pointdata and previous point & pointdata */
PointIdentifier nextID = it->first;
if (this->SwapPointContents(nextID, currentID, timeStep) == true)
this->Modified();
}
break;
default:
itkWarningMacro("mitkPointSet could not understrand the operation. Please check!");
break;
}
// to tell the mappers, that the data is modified and has to be updated
// only call modified if anything is done, so call in cases
// this->Modified();
mitk::OperationEndEvent endevent(operation);
((const itk::Object *)this)->InvokeEvent(endevent);
//*todo has to be done here, cause of update-pipeline not working yet
// As discussed lately, don't mess with the rendering from inside data structures
// mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::PointSet::UpdateOutputInformation()
{
if (this->GetSource())
{
this->GetSource()->UpdateOutputInformation();
}
//
// first make sure, that the associated time sliced geometry has
// the same number of geometry 3d's as PointSets are present
//
TimeGeometry *timeGeometry = GetTimeGeometry();
if (timeGeometry->CountTimeSteps() != m_PointSetSeries.size())
{
itkExceptionMacro(<< "timeGeometry->CountTimeSteps() != m_PointSetSeries.size() -- use Initialize(timeSteps) with "
"correct number of timeSteps!");
}
// This is needed to detect zero objects
mitk::ScalarType nullpoint[] = {0, 0, 0, 0, 0, 0};
BoundingBox::BoundsArrayType itkBoundsNull(nullpoint);
//
// Iterate over the PointSets and update the Geometry
// information of each of the items.
//
if (m_CalculateBoundingBox)
{
for (unsigned int i = 0; i < m_PointSetSeries.size(); ++i)
{
const DataType::BoundingBoxType *bb = m_PointSetSeries[i]->GetBoundingBox();
BoundingBox::BoundsArrayType itkBounds = bb->GetBounds();
if (m_PointSetSeries[i].IsNull() || (m_PointSetSeries[i]->GetNumberOfPoints() == 0) ||
(itkBounds == itkBoundsNull))
{
itkBounds = itkBoundsNull;
continue;
}
// Ensure minimal bounds of 1.0 in each dimension
for (unsigned int j = 0; j < 3; ++j)
{
if (itkBounds[j * 2 + 1] - itkBounds[j * 2] < 1.0)
{
BoundingBox::CoordRepType center = (itkBounds[j * 2] + itkBounds[j * 2 + 1]) / 2.0;
itkBounds[j * 2] = center - 0.5;
itkBounds[j * 2 + 1] = center + 0.5;
}
}
this->GetGeometry(i)->SetBounds(itkBounds);
}
m_CalculateBoundingBox = false;
}
this->GetTimeGeometry()->Update();
}
void mitk::PointSet::SetRequestedRegionToLargestPossibleRegion()
{
}
bool mitk::PointSet::RequestedRegionIsOutsideOfTheBufferedRegion()
{
return false;
}
bool mitk::PointSet::VerifyRequestedRegion()
{
return true;
}
void mitk::PointSet::SetRequestedRegion(const DataObject *)
{
}
void mitk::PointSet::PrintSelf(std::ostream &os, itk::Indent indent) const
{
Superclass::PrintSelf(os, indent);
os << indent << "Number timesteps: " << m_PointSetSeries.size() << "\n";
unsigned int i = 0;
for (auto it = m_PointSetSeries.begin(); it != m_PointSetSeries.end(); ++it)
{
os << indent << "Timestep " << i++ << ": \n";
MeshType::Pointer ps = *it;
itk::Indent nextIndent = indent.GetNextIndent();
ps->Print(os, nextIndent);
MeshType::PointsContainer *points = ps->GetPoints();
MeshType::PointDataContainer *datas = ps->GetPointData();
MeshType::PointDataContainer::Iterator dataIterator = datas->Begin();
for (MeshType::PointsContainer::Iterator pointIterator = points->Begin(); pointIterator != points->End();
++pointIterator, ++dataIterator)
{
os << nextIndent << "Point " << pointIterator->Index() << ": [";
os << pointIterator->Value().GetElement(0);
for (unsigned int i = 1; i < PointType::GetPointDimension(); ++i)
{
os << ", " << pointIterator->Value().GetElement(i);
}
os << "]";
os << ", selected: " << dataIterator->Value().selected << ", point spec: " << dataIterator->Value().pointSpec
<< "\n";
}
}
}
bool mitk::PointSet::SwapPointContents(PointIdentifier id1, PointIdentifier id2, int timeStep)
{
/* search and cache contents */
PointType p1;
if (m_PointSetSeries[timeStep]->GetPoint(id1, &p1) == false)
return false;
PointDataType data1;
if (m_PointSetSeries[timeStep]->GetPointData(id1, &data1) == false)
return false;
PointType p2;
if (m_PointSetSeries[timeStep]->GetPoint(id2, &p2) == false)
return false;
PointDataType data2;
if (m_PointSetSeries[timeStep]->GetPointData(id2, &data2) == false)
return false;
/* now swap contents */
m_PointSetSeries[timeStep]->SetPoint(id1, p2);
m_PointSetSeries[timeStep]->SetPointData(id1, data2);
m_PointSetSeries[timeStep]->SetPoint(id2, p1);
m_PointSetSeries[timeStep]->SetPointData(id2, data1);
return true;
}
bool mitk::PointSet::PointDataType::operator==(const mitk::PointSet::PointDataType &other) const
{
return id == other.id && selected == other.selected && pointSpec == other.pointSpec;
}
bool mitk::Equal(const mitk::PointSet *leftHandSide,
const mitk::PointSet *rightHandSide,
mitk::ScalarType eps,
bool verbose,
bool checkGeometry)
{
if ((leftHandSide == nullptr) || (rightHandSide == nullptr))
{
MITK_ERROR << "mitk::Equal( const mitk::PointSet* leftHandSide, const mitk::PointSet* rightHandSide, "
"mitk::ScalarType eps, bool verbose ) does not work with nullptr pointer input.";
return false;
}
return Equal(*leftHandSide, *rightHandSide, eps, verbose, checkGeometry);
}
bool mitk::Equal(const mitk::PointSet &leftHandSide,
const mitk::PointSet &rightHandSide,
mitk::ScalarType eps,
bool verbose,
bool checkGeometry)
{
bool result = true;
// If comparing point sets from file, you must not compare the geometries, as they are not saved. In other cases, you
// do need to check them.
if (checkGeometry)
{
if (!mitk::Equal(*leftHandSide.GetGeometry(), *rightHandSide.GetGeometry(), eps, verbose))
{
if (verbose)
MITK_INFO << "[( PointSet )] Geometries differ.";
result = false;
}
}
if (leftHandSide.GetSize() != rightHandSide.GetSize())
{
if (verbose)
MITK_INFO << "[( PointSet )] Number of points differ.";
result = false;
}
else
{
// if the size is equal, we compare the point values
mitk::Point3D pointLeftHandSide;
mitk::Point3D pointRightHandSide;
int numberOfIncorrectPoints = 0;
// Iterate over both pointsets in order to compare all points pair-wise
mitk::PointSet::PointsConstIterator end = leftHandSide.End();
for (mitk::PointSet::PointsConstIterator pointSetIteratorLeft = leftHandSide.Begin(),
pointSetIteratorRight = rightHandSide.Begin();
pointSetIteratorLeft != end;
++pointSetIteratorLeft, ++pointSetIteratorRight) // iterate simultaneously over both sets
{
pointLeftHandSide = pointSetIteratorLeft.Value();
pointRightHandSide = pointSetIteratorRight.Value();
if (!mitk::Equal(pointLeftHandSide, pointRightHandSide, eps, verbose))
{
if (verbose)
MITK_INFO << "[( PointSet )] Point values are different.";
result = false;
numberOfIncorrectPoints++;
}
}
if ((numberOfIncorrectPoints > 0) && verbose)
{
MITK_INFO << numberOfIncorrectPoints << " of a total of " << leftHandSide.GetSize() << " points are different.";
}
}
return result;
}
diff --git a/Modules/Core/src/DataManagement/mitkPropertyList.cpp b/Modules/Core/src/DataManagement/mitkPropertyList.cpp
index d9ad1ac009..994d5af78c 100644
--- a/Modules/Core/src/DataManagement/mitkPropertyList.cpp
+++ b/Modules/Core/src/DataManagement/mitkPropertyList.cpp
@@ -1,376 +1,376 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkPropertyList.h"
#include "mitkNumericTypes.h"
#include "mitkProperties.h"
#include "mitkStringProperty.h"
mitk::BaseProperty::ConstPointer mitk::PropertyList::GetConstProperty(const std::string &propertyKey, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) const
{
PropertyMap::const_iterator it;
it = m_Properties.find(propertyKey);
if (it != m_Properties.cend())
return it->second.GetPointer();
else
return nullptr;
};
std::vector<std::string> mitk::PropertyList::GetPropertyKeys(const std::string &contextName, bool includeDefaultContext) const
{
std::vector<std::string> propertyKeys;
if (contextName.empty() || includeDefaultContext)
{
for (const auto &property : this->m_Properties)
propertyKeys.push_back(property.first);
}
return propertyKeys;
};
std::vector<std::string> mitk::PropertyList::GetPropertyContextNames() const
{
return std::vector<std::string>();
};
mitk::BaseProperty *mitk::PropertyList::GetProperty(const std::string &propertyKey) const
{
PropertyMap::const_iterator it;
it = m_Properties.find(propertyKey);
if (it != m_Properties.cend())
return it->second;
else
return nullptr;
}
mitk::BaseProperty * mitk::PropertyList::GetNonConstProperty(const std::string &propertyKey, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/)
{
return this->GetProperty(propertyKey);
}
void mitk::PropertyList::SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/)
{
if (propertyKey.empty())
mitkThrow() << "Property key is empty.";
if (!property)
return;
// make sure that BaseProperty*, which may have just been created and never been
// assigned to a SmartPointer, is registered/unregistered properly. If we do not
// do that, it will a) not deleted in case it is identical to the old one or
// b) possibly deleted when temporarily added to a smartpointer somewhere below.
BaseProperty::Pointer tmpSmartPointerToProperty = property;
auto it(m_Properties.find(propertyKey));
// Is a property with key @a propertyKey contained in the list?
if (it != m_Properties.cend())
{
// yes
// is the property contained in the list identical to the new one?
if (it->second->operator==(*property))
{
// yes? do nothing and return.
return;
}
if (it->second->AssignProperty(*property))
{
// The assignment was successfull
this->Modified();
}
else
{
MITK_ERROR << "In " __FILE__ ", l." << __LINE__ << ": Trying to set existing property " << it->first
<< " of type " << it->second->GetNameOfClass() << " to a property with different type "
<< property->GetNameOfClass() << "."
<< " Use ReplaceProperty() instead." << std::endl;
}
return;
}
// no? add it.
m_Properties.insert(PropertyMap::value_type(propertyKey, property));
this->Modified();
}
void mitk::PropertyList::ReplaceProperty(const std::string &propertyKey, BaseProperty *property)
{
if (!property)
return;
auto it(m_Properties.find(propertyKey));
// Is a property with key @a propertyKey contained in the list?
if (it != m_Properties.cend())
{
it->second = nullptr;
m_Properties.erase(it);
}
// no? add/replace it.
m_Properties.insert(PropertyMap::value_type(propertyKey, property));
Modified();
}
void mitk::PropertyList::RemoveProperty(const std::string &propertyKey, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/)
{
auto it(m_Properties.find(propertyKey));
// Is a property with key @a propertyKey contained in the list?
if (it != m_Properties.cend())
{
it->second = nullptr;
m_Properties.erase(it);
Modified();
}
}
mitk::PropertyList::PropertyList()
{
}
mitk::PropertyList::PropertyList(const mitk::PropertyList &other) : itk::Object()
{
for (auto i = other.m_Properties.cbegin(); i != other.m_Properties.cend(); ++i)
{
m_Properties.insert(std::make_pair(i->first, i->second->Clone()));
}
}
mitk::PropertyList::~PropertyList()
{
Clear();
}
/**
* Consider the list as changed when any of the properties has changed recently.
*/
-unsigned long mitk::PropertyList::GetMTime() const
+itk::ModifiedTimeType mitk::PropertyList::GetMTime() const
{
for (auto it = m_Properties.cbegin(); it != m_Properties.cend(); ++it)
{
if (it->second.IsNull())
{
itkWarningMacro(<< "Property '" << it->first << "' contains nothing (nullptr).");
continue;
}
if (Superclass::GetMTime() < it->second->GetMTime())
{
Modified();
break;
}
}
return Superclass::GetMTime();
}
bool mitk::PropertyList::DeleteProperty(const std::string &propertyKey)
{
auto it = m_Properties.find(propertyKey);
if (it != m_Properties.end())
{
it->second = nullptr;
m_Properties.erase(it);
Modified();
return true;
}
return false;
}
void mitk::PropertyList::Clear()
{
auto it = m_Properties.begin(), end = m_Properties.end();
while (it != end)
{
it->second = nullptr;
++it;
}
m_Properties.clear();
}
itk::LightObject::Pointer mitk::PropertyList::InternalClone() const
{
itk::LightObject::Pointer result(new Self(*this));
result->UnRegister();
return result;
}
void mitk::PropertyList::ConcatenatePropertyList(PropertyList *pList, bool replace)
{
if (pList)
{
const PropertyMap *propertyMap = pList->GetMap();
for (auto iter = propertyMap->cbegin(); // m_PropertyList is created in the constructor, so we don't check it here
iter != propertyMap->cend();
++iter)
{
const std::string key = iter->first;
BaseProperty *value = iter->second;
if (replace)
{
ReplaceProperty(key.c_str(), value);
}
else
{
SetProperty(key.c_str(), value);
}
}
}
}
bool mitk::PropertyList::GetBoolProperty(const char *propertyKey, bool &boolValue) const
{
BoolProperty *gp = dynamic_cast<BoolProperty *>(GetProperty(propertyKey));
if (gp != nullptr)
{
boolValue = gp->GetValue();
return true;
}
return false;
// Templated Method does not work on Macs
// return GetPropertyValue<bool>(propertyKey, boolValue);
}
bool mitk::PropertyList::GetIntProperty(const char *propertyKey, int &intValue) const
{
IntProperty *gp = dynamic_cast<IntProperty *>(GetProperty(propertyKey));
if (gp != nullptr)
{
intValue = gp->GetValue();
return true;
}
return false;
// Templated Method does not work on Macs
// return GetPropertyValue<int>(propertyKey, intValue);
}
bool mitk::PropertyList::GetFloatProperty(const char *propertyKey, float &floatValue) const
{
FloatProperty *gp = dynamic_cast<FloatProperty *>(GetProperty(propertyKey));
if (gp != nullptr)
{
floatValue = gp->GetValue();
return true;
}
return false;
// Templated Method does not work on Macs
// return GetPropertyValue<float>(propertyKey, floatValue);
}
bool mitk::PropertyList::GetStringProperty(const char *propertyKey, std::string &stringValue) const
{
StringProperty *sp = dynamic_cast<StringProperty *>(GetProperty(propertyKey));
if (sp != nullptr)
{
stringValue = sp->GetValue();
return true;
}
return false;
}
void mitk::PropertyList::SetIntProperty(const char *propertyKey, int intValue)
{
SetProperty(propertyKey, mitk::IntProperty::New(intValue));
}
void mitk::PropertyList::SetBoolProperty(const char *propertyKey, bool boolValue)
{
SetProperty(propertyKey, mitk::BoolProperty::New(boolValue));
}
void mitk::PropertyList::SetFloatProperty(const char *propertyKey, float floatValue)
{
SetProperty(propertyKey, mitk::FloatProperty::New(floatValue));
}
void mitk::PropertyList::SetStringProperty(const char *propertyKey, const char *stringValue)
{
SetProperty(propertyKey, mitk::StringProperty::New(stringValue));
}
void mitk::PropertyList::Set(const char *propertyKey, bool boolValue)
{
this->SetBoolProperty(propertyKey, boolValue);
}
void mitk::PropertyList::Set(const char *propertyKey, int intValue)
{
this->SetIntProperty(propertyKey, intValue);
}
void mitk::PropertyList::Set(const char *propertyKey, float floatValue)
{
this->SetFloatProperty(propertyKey, floatValue);
}
void mitk::PropertyList::Set(const char *propertyKey, double doubleValue)
{
this->SetDoubleProperty(propertyKey, doubleValue);
}
void mitk::PropertyList::Set(const char *propertyKey, const char *stringValue)
{
this->SetStringProperty(propertyKey, stringValue);
}
void mitk::PropertyList::Set(const char *propertyKey, const std::string &stringValue)
{
this->SetStringProperty(propertyKey, stringValue.c_str());
}
bool mitk::PropertyList::Get(const char *propertyKey, bool &boolValue) const
{
return this->GetBoolProperty(propertyKey, boolValue);
}
bool mitk::PropertyList::Get(const char *propertyKey, int &intValue) const
{
return this->GetIntProperty(propertyKey, intValue);
}
bool mitk::PropertyList::Get(const char *propertyKey, float &floatValue) const
{
return this->GetFloatProperty(propertyKey, floatValue);
}
bool mitk::PropertyList::Get(const char *propertyKey, double &doubleValue) const
{
return this->GetDoubleProperty(propertyKey, doubleValue);
}
bool mitk::PropertyList::Get(const char *propertyKey, std::string &stringValue) const
{
return this->GetStringProperty(propertyKey, stringValue);
}
bool mitk::PropertyList::GetDoubleProperty(const char *propertyKey, double &doubleValue) const
{
DoubleProperty *gp = dynamic_cast<DoubleProperty *>(GetProperty(propertyKey));
if (gp != nullptr)
{
doubleValue = gp->GetValue();
return true;
}
return false;
}
void mitk::PropertyList::SetDoubleProperty(const char *propertyKey, double doubleValue)
{
SetProperty(propertyKey, mitk::DoubleProperty::New(doubleValue));
}
diff --git a/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp b/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp
index 6b69557d23..4eedfcceeb 100644
--- a/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp
+++ b/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp
@@ -1,258 +1,256 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkStandaloneDataStorage.h"
-#include "itkMutexLockHolder.h"
-#include "itkSimpleFastMutexLock.h"
#include "mitkDataNode.h"
#include "mitkGroupTagProperty.h"
#include "mitkNodePredicateBase.h"
#include "mitkNodePredicateProperty.h"
#include "mitkProperties.h"
mitk::StandaloneDataStorage::StandaloneDataStorage() : mitk::DataStorage()
{
}
mitk::StandaloneDataStorage::~StandaloneDataStorage()
{
for (auto it = m_SourceNodes.begin(); it != m_SourceNodes.end(); ++it)
{
this->RemoveListeners(it->first);
}
}
bool mitk::StandaloneDataStorage::IsInitialized() const
{
return true;
}
void mitk::StandaloneDataStorage::Add(mitk::DataNode *node, const mitk::DataStorage::SetOfObjects *parents)
{
{
- itk::MutexLockHolder<itk::SimpleFastMutexLock> locked(m_Mutex);
+ std::lock_guard<std::mutex> locked(m_Mutex);
if (!IsInitialized())
throw std::logic_error("DataStorage not initialized");
/* check if node is in its own list of sources */
if ((parents != nullptr) && (std::find(parents->begin(), parents->end(), node) != parents->end()))
throw std::invalid_argument("Node is it's own parent");
/* check if node already exists in StandaloneDataStorage */
if (m_SourceNodes.find(node) != m_SourceNodes.end())
throw std::invalid_argument("Node is already in DataStorage");
/* create parent list if it does not exist */
mitk::DataStorage::SetOfObjects::ConstPointer sp;
if (parents != nullptr)
sp = parents;
else
sp = mitk::DataStorage::SetOfObjects::New();
/* Store node and parent list in sources adjacency list */
m_SourceNodes.insert(std::make_pair(node, sp));
/* Store node and an empty children list in derivations adjacency list */
mitk::DataStorage::SetOfObjects::Pointer childrenPointer = mitk::DataStorage::SetOfObjects::New();
mitk::DataStorage::SetOfObjects::ConstPointer children = childrenPointer.GetPointer();
m_DerivedNodes.insert(std::make_pair(node, children));
/* create entry in derivations adjacency list for each parent of the new node */
for (SetOfObjects::ConstIterator it = sp->Begin(); it != sp->End(); it++)
{
mitk::DataNode::ConstPointer parent = it.Value().GetPointer();
mitk::DataStorage::SetOfObjects::ConstPointer derivedObjects =
m_DerivedNodes[parent]; // get or create pointer to list of derived objects for that parent node
if (derivedObjects.IsNull())
m_DerivedNodes[parent] =
mitk::DataStorage::SetOfObjects::New(); // Create a set of Objects, if it does not already exist
auto *deob = const_cast<mitk::DataStorage::SetOfObjects *>(
m_DerivedNodes[parent].GetPointer()); // temporarily get rid of const pointer to insert new element
deob->InsertElement(deob->Size(),
node); // node is derived from parent. Insert it into the parents list of derived objects
}
// register for ITK changed events
this->AddListeners(node);
}
/* Notify observers */
EmitAddNodeEvent(node);
}
void mitk::StandaloneDataStorage::Remove(const mitk::DataNode *node)
{
if (!IsInitialized())
throw std::logic_error("DataStorage not initialized");
if (node == nullptr)
return;
// remove ITK modified event listener
this->RemoveListeners(node);
// muellerm, 22.9.10: add additional reference count to ensure
// that the node is not deleted when removed from the relation map
// while m_Mutex is locked. This would cause the an itk::DeleteEvent
// is thrown and a deadlock will occur when event receivers
// access the DataStorage again in their event processing function
//
mitk::DataNode::ConstPointer nodeGuard(node);
/* Notify observers of imminent node removal */
EmitRemoveNodeEvent(node);
{
- itk::MutexLockHolder<itk::SimpleFastMutexLock> locked(m_Mutex);
+ std::lock_guard<std::mutex> locked(m_Mutex);
/* remove node from both relation adjacency lists */
this->RemoveFromRelation(node, m_SourceNodes);
this->RemoveFromRelation(node, m_DerivedNodes);
}
}
bool mitk::StandaloneDataStorage::Exists(const mitk::DataNode *node) const
{
- itk::MutexLockHolder<itk::SimpleFastMutexLock> locked(m_Mutex);
+ std::lock_guard<std::mutex> locked(m_Mutex);
return (m_SourceNodes.find(node) != m_SourceNodes.end());
}
void mitk::StandaloneDataStorage::RemoveFromRelation(const mitk::DataNode *node, AdjacencyList &relation)
{
for (auto mapIter = relation.cbegin(); mapIter != relation.cend();
++mapIter) // for each node in the relation
if (mapIter->second.IsNotNull()) // if node has a relation list
{
SetOfObjects::Pointer s =
const_cast<SetOfObjects *>(mapIter->second.GetPointer()); // search for node to be deleted in the relation list
auto relationListIter = std::find(
s->begin(),
s->end(),
node); // this assumes, that the relation list does not contain duplicates (which should be safe to assume)
if (relationListIter != s->end()) // if node to be deleted is in relation list
s->erase(relationListIter); // remove it from parentlist
}
/* now remove node from the relation */
AdjacencyList::iterator adIt;
adIt = relation.find(node);
if (adIt != relation.end())
relation.erase(adIt);
}
mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetAll() const
{
- itk::MutexLockHolder<itk::SimpleFastMutexLock> locked(m_Mutex);
+ std::lock_guard<std::mutex> locked(m_Mutex);
if (!IsInitialized())
throw std::logic_error("DataStorage not initialized");
mitk::DataStorage::SetOfObjects::Pointer resultset = mitk::DataStorage::SetOfObjects::New();
/* Fill resultset with all objects that are managed by the StandaloneDataStorage object */
unsigned int index = 0;
for (auto it = m_SourceNodes.cbegin(); it != m_SourceNodes.cend(); ++it)
if (it->first.IsNull())
continue;
else
resultset->InsertElement(index++, const_cast<mitk::DataNode *>(it->first.GetPointer()));
return SetOfObjects::ConstPointer(resultset);
}
mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetRelations(
const mitk::DataNode *node,
const AdjacencyList &relation,
const NodePredicateBase *condition,
bool onlyDirectlyRelated) const
{
if (node == nullptr)
throw std::invalid_argument("invalid node");
/* Either read direct relations directly from adjacency list */
if (onlyDirectlyRelated)
{
auto it = relation.find(node); // get parents of current node
if ((it == relation.cend()) || (it->second.IsNull())) // node not found in list or no set of parents
return SetOfObjects::ConstPointer(mitk::DataStorage::SetOfObjects::New()); // return an empty set
else
return this->FilterSetOfObjects(it->second, condition);
}
/* Or traverse adjacency list to collect all related nodes */
std::vector<mitk::DataNode::ConstPointer> resultset;
std::vector<mitk::DataNode::ConstPointer> openlist;
/* Initialize openlist with node. this will add node to resultset,
but that is necessary to detect circular relations that would lead to endless recursion */
openlist.push_back(node);
while (openlist.size() > 0)
{
mitk::DataNode::ConstPointer current = openlist.back(); // get element that needs to be processed
openlist.pop_back(); // remove last element, because it gets processed now
resultset.push_back(current); // add current element to resultset
auto it = relation.find(current); // get parents of current node
if ((it == relation.cend()) // if node not found in list
||
(it->second.IsNull()) // or no set of parents available
||
(it->second->Size() == 0)) // or empty set of parents
continue; // then continue with next node in open list
else
for (SetOfObjects::ConstIterator parentIt = it->second->Begin(); parentIt != it->second->End();
++parentIt) // for each parent of current node
{
mitk::DataNode::ConstPointer p = parentIt.Value().GetPointer();
if (!(std::find(resultset.cbegin(), resultset.cend(), p) !=
resultset.end()) // if it is not already in resultset
&&
!(std::find(openlist.cbegin(), openlist.cend(), p) != openlist.cend())) // and not already in openlist
openlist.push_back(p); // then add it to openlist, so that it can be processed
}
}
/* now finally copy the results to a proper SetOfObjects variable exluding the initial node and checking the condition
* if any is given */
mitk::DataStorage::SetOfObjects::Pointer realResultset = mitk::DataStorage::SetOfObjects::New();
if (condition != nullptr)
{
for (auto resultIt = resultset.cbegin();
resultIt != resultset.cend();
++resultIt)
if ((*resultIt != node) && (condition->CheckNode(*resultIt) == true))
realResultset->InsertElement(realResultset->Size(),
mitk::DataNode::Pointer(const_cast<mitk::DataNode *>((*resultIt).GetPointer())));
}
else
{
for (auto resultIt = resultset.cbegin();
resultIt != resultset.cend();
++resultIt)
if (*resultIt != node)
realResultset->InsertElement(realResultset->Size(),
mitk::DataNode::Pointer(const_cast<mitk::DataNode *>((*resultIt).GetPointer())));
}
return SetOfObjects::ConstPointer(realResultset);
}
mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetSources(
const mitk::DataNode *node, const NodePredicateBase *condition, bool onlyDirectSources) const
{
- itk::MutexLockHolder<itk::SimpleFastMutexLock> locked(m_Mutex);
+ std::lock_guard<std::mutex> locked(m_Mutex);
return this->GetRelations(node, m_SourceNodes, condition, onlyDirectSources);
}
mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetDerivations(
const mitk::DataNode *node, const NodePredicateBase *condition, bool onlyDirectDerivations) const
{
- itk::MutexLockHolder<itk::SimpleFastMutexLock> locked(m_Mutex);
+ std::lock_guard<std::mutex> locked(m_Mutex);
return this->GetRelations(node, m_DerivedNodes, condition, onlyDirectDerivations);
}
void mitk::StandaloneDataStorage::PrintSelf(std::ostream &os, itk::Indent indent) const
{
os << indent << "StandaloneDataStorage:\n";
Superclass::PrintSelf(os, indent);
}
diff --git a/Modules/Core/src/IO/mitkAbstractFileReader.cpp b/Modules/Core/src/IO/mitkAbstractFileReader.cpp
index 4ac0024740..3085cb6efd 100644
--- a/Modules/Core/src/IO/mitkAbstractFileReader.cpp
+++ b/Modules/Core/src/IO/mitkAbstractFileReader.cpp
@@ -1,334 +1,334 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <mitkAbstractFileReader.h>
#include <mitkCustomMimeType.h>
#include <mitkIOUtil.h>
+#include <mitkUtf8Util.h>
#include <mitkFileReaderWriterBase.h>
#include <mitkVersion.h>
#include <mitkIOMetaInformationPropertyConstants.h>
#include <usGetModuleContext.h>
#include <usModuleContext.h>
#include <usPrototypeServiceFactory.h>
#include <itksys/SystemTools.hxx>
#include <fstream>
namespace mitk
{
AbstractFileReader::InputStream::InputStream(IFileReader *reader, std::ios_base::openmode mode)
: std::istream(nullptr), m_Stream(nullptr)
{
std::istream *stream = reader->GetInputStream();
if (stream)
{
this->init(stream->rdbuf());
}
else
{
m_Stream = new std::ifstream(reader->GetInputLocation().c_str(), mode);
this->init(m_Stream->rdbuf());
}
}
AbstractFileReader::InputStream::~InputStream() { delete m_Stream; }
class AbstractFileReader::Impl : public FileReaderWriterBase
{
public:
Impl() : FileReaderWriterBase(), m_Stream(nullptr), m_PrototypeFactory(nullptr) {}
Impl(const Impl &other) : FileReaderWriterBase(other), m_Stream(nullptr), m_PrototypeFactory(nullptr) {}
std::string m_Location;
std::string m_TmpFile;
std::istream *m_Stream;
us::PrototypeServiceFactory *m_PrototypeFactory;
us::ServiceRegistration<IFileReader> m_Reg;
};
AbstractFileReader::AbstractFileReader() : d(new Impl) {}
AbstractFileReader::~AbstractFileReader()
{
UnregisterService();
delete d->m_PrototypeFactory;
if (!d->m_TmpFile.empty())
{
std::remove(d->m_TmpFile.c_str());
}
}
AbstractFileReader::AbstractFileReader(const AbstractFileReader &other) : IFileReader(), d(new Impl(*other.d.get()))
{
}
AbstractFileReader::AbstractFileReader(const CustomMimeType &mimeType, const std::string &description) : d(new Impl)
{
d->SetMimeType(mimeType);
d->SetDescription(description);
}
////////////////////// Reading /////////////////////////
std::vector<BaseData::Pointer> AbstractFileReader::Read()
{
std::vector<BaseData::Pointer> result = this->DoRead();
const auto options = this->GetOptions();
for (auto& data : result)
{
data->SetProperty(PropertyKeyPathToPropertyName(IOMetaInformationPropertyConstants::READER_DESCRIPTION()), StringProperty::New(d->GetDescription()));
data->SetProperty(PropertyKeyPathToPropertyName(IOMetaInformationPropertyConstants::READER_VERSION()), StringProperty::New(MITK_VERSION_STRING));
data->SetProperty(PropertyKeyPathToPropertyName(IOMetaInformationPropertyConstants::READER_MIME_NAME()), StringProperty::New(d->GetMimeType()->GetName()));
data->SetProperty(PropertyKeyPathToPropertyName(IOMetaInformationPropertyConstants::READER_MIME_CATEGORY()), StringProperty::New(d->GetMimeType()->GetCategory()));
if (this->GetInputStream() == nullptr)
{
- data->SetProperty(PropertyKeyPathToPropertyName(IOMetaInformationPropertyConstants::READER_INPUTLOCATION()), StringProperty::New(IOUtil::Local8BitToUtf8(this->GetInputLocation())));
+ data->SetProperty(PropertyKeyPathToPropertyName(IOMetaInformationPropertyConstants::READER_INPUTLOCATION()), StringProperty::New(Utf8Util::Local8BitToUtf8(this->GetInputLocation())));
}
for (const auto& option : options)
{
auto optionpath = IOMetaInformationPropertyConstants::READER_OPTION_ROOT().AddElement(option.first);
data->SetProperty(PropertyKeyPathToPropertyName(optionpath), StringProperty::New(option.second.ToString()));
}
}
return result;
}
DataStorage::SetOfObjects::Pointer AbstractFileReader::Read(DataStorage &ds)
{
DataStorage::SetOfObjects::Pointer result = DataStorage::SetOfObjects::New();
std::vector<BaseData::Pointer> data = this->Read();
for (auto iter = data.begin(); iter != data.end(); ++iter)
{
mitk::DataNode::Pointer node = mitk::DataNode::New();
node->SetData(*iter);
this->SetDefaultDataNodeProperties(node, this->GetInputLocation());
ds.Add(node);
result->InsertElement(result->Size(), node);
}
return result;
}
IFileReader::ConfidenceLevel AbstractFileReader::GetConfidenceLevel() const
{
if (d->m_Stream)
{
if (*d->m_Stream)
return Supported;
}
else
{
- if (itksys::SystemTools::FileExists(this->GetInputLocation().c_str(), true))
+ if (itksys::SystemTools::FileExists(Utf8Util::Local8BitToUtf8(this->GetInputLocation()).c_str(), true))
{
return Supported;
}
}
return Unsupported;
}
//////////// µS Registration & Properties //////////////
us::ServiceRegistration<IFileReader> AbstractFileReader::RegisterService(us::ModuleContext *context)
{
if (d->m_PrototypeFactory)
return us::ServiceRegistration<IFileReader>();
if (context == nullptr)
{
context = us::GetModuleContext();
}
d->RegisterMimeType(context);
if (this->GetMimeType()->GetName().empty())
{
MITK_WARN << "Not registering reader due to empty MIME type.";
return us::ServiceRegistration<IFileReader>();
}
struct PrototypeFactory : public us::PrototypeServiceFactory
{
AbstractFileReader *const m_Prototype;
PrototypeFactory(AbstractFileReader *prototype) : m_Prototype(prototype) {}
us::InterfaceMap GetService(us::Module * /*module*/,
const us::ServiceRegistrationBase & /*registration*/) override
{
return us::MakeInterfaceMap<IFileReader>(m_Prototype->Clone());
}
void UngetService(us::Module * /*module*/,
const us::ServiceRegistrationBase & /*registration*/,
const us::InterfaceMap &service) override
{
delete us::ExtractInterface<IFileReader>(service);
}
};
d->m_PrototypeFactory = new PrototypeFactory(this);
us::ServiceProperties props = this->GetServiceProperties();
d->m_Reg = context->RegisterService<IFileReader>(d->m_PrototypeFactory, props);
return d->m_Reg;
}
void AbstractFileReader::UnregisterService()
{
try
{
d->m_Reg.Unregister();
}
catch (const std::exception &)
{
}
}
us::ServiceProperties AbstractFileReader::GetServiceProperties() const
{
us::ServiceProperties result;
result[IFileReader::PROP_DESCRIPTION()] = this->GetDescription();
result[IFileReader::PROP_MIMETYPE()] = this->GetMimeType()->GetName();
result[us::ServiceConstants::SERVICE_RANKING()] = this->GetRanking();
return result;
}
us::ServiceRegistration<CustomMimeType> AbstractFileReader::RegisterMimeType(us::ModuleContext *context)
{
return d->RegisterMimeType(context);
}
std::vector< std::string > AbstractFileReader::GetReadFiles(){ return m_ReadFiles; }
void AbstractFileReader::SetMimeType(const CustomMimeType &mimeType) { d->SetMimeType(mimeType); }
void AbstractFileReader::SetDescription(const std::string &description) { d->SetDescription(description); }
void AbstractFileReader::SetRanking(int ranking) { d->SetRanking(ranking); }
int AbstractFileReader::GetRanking() const { return d->GetRanking(); }
std::string AbstractFileReader::GetLocalFileName() const
{
std::string localFileName;
if (d->m_Stream)
{
if (d->m_TmpFile.empty())
{
// write the stream contents to temporary file
- std::string ext = itksys::SystemTools::GetFilenameExtension(this->GetInputLocation());
+ std::string ext = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetFilenameExtension(Utf8Util::Local8BitToUtf8(this->GetInputLocation())));
std::ofstream tmpStream;
localFileName = mitk::IOUtil::CreateTemporaryFile(
tmpStream, std::ios_base::out | std::ios_base::trunc | std::ios_base::binary, "XXXXXX" + ext);
tmpStream << d->m_Stream->rdbuf();
d->m_TmpFile = localFileName;
}
else
{
localFileName = d->m_TmpFile;
}
}
else
{
localFileName = d->m_Location;
}
return localFileName;
}
//////////////////////// Options ///////////////////////
void AbstractFileReader::SetDefaultOptions(const IFileReader::Options &defaultOptions)
{
d->SetDefaultOptions(defaultOptions);
}
IFileReader::Options AbstractFileReader::GetDefaultOptions() const { return d->GetDefaultOptions(); }
void AbstractFileReader::SetInput(const std::string &location)
{
d->m_Location = location;
d->m_Stream = nullptr;
}
void AbstractFileReader::SetInput(const std::string &location, std::istream *is)
{
if (d->m_Stream != is && !d->m_TmpFile.empty())
{
std::remove(d->m_TmpFile.c_str());
d->m_TmpFile.clear();
}
d->m_Location = location;
d->m_Stream = is;
}
std::string AbstractFileReader::GetInputLocation() const { return d->m_Location; }
std::istream *AbstractFileReader::GetInputStream() const { return d->m_Stream; }
MimeType AbstractFileReader::GetRegisteredMimeType() const { return d->GetRegisteredMimeType(); }
IFileReader::Options AbstractFileReader::GetOptions() const { return d->GetOptions(); }
us::Any AbstractFileReader::GetOption(const std::string &name) const { return d->GetOption(name); }
void AbstractFileReader::SetOptions(const Options &options) { d->SetOptions(options); }
void AbstractFileReader::SetOption(const std::string &name, const us::Any &value) { d->SetOption(name, value); }
////////////////// MISC //////////////////
void AbstractFileReader::AddProgressCallback(const ProgressCallback &callback) { d->AddProgressCallback(callback); }
void AbstractFileReader::RemoveProgressCallback(const ProgressCallback &callback)
{
d->RemoveProgressCallback(callback);
}
////////////////// µS related Getters //////////////////
const CustomMimeType *AbstractFileReader::GetMimeType() const { return d->GetMimeType(); }
void AbstractFileReader::SetMimeTypePrefix(const std::string &prefix) { d->SetMimeTypePrefix(prefix); }
std::string AbstractFileReader::GetMimeTypePrefix() const { return d->GetMimeTypePrefix(); }
std::string AbstractFileReader::GetDescription() const { return d->GetDescription(); }
void AbstractFileReader::SetDefaultDataNodeProperties(DataNode *node, const std::string &filePath)
{
// path
if (!filePath.empty())
{
- auto path = itksys::SystemTools::GetFilenamePath(filePath);
- path = IOUtil::Local8BitToUtf8(path);
+ auto path = itksys::SystemTools::GetFilenamePath(Utf8Util::Local8BitToUtf8(filePath));
node->SetProperty(StringProperty::PATH, mitk::StringProperty::New(path));
}
// name already defined?
mitk::StringProperty::Pointer nameProp = dynamic_cast<mitk::StringProperty *>(node->GetProperty("name"));
if (nameProp.IsNull() || nameProp->GetValue() == DataNode::NO_NAME_VALUE())
{
// name already defined in BaseData
mitk::StringProperty::Pointer baseDataNameProp =
dynamic_cast<mitk::StringProperty *>(node->GetData()->GetProperty("name").GetPointer());
if (baseDataNameProp.IsNull() || baseDataNameProp->GetValue() == DataNode::NO_NAME_VALUE())
{
// name neither defined in node, nor in BaseData -> name = filebasename;
auto name = this->GetRegisteredMimeType().GetFilenameWithoutExtension(filePath);
- name = IOUtil::Local8BitToUtf8(name);
+ name = Utf8Util::Local8BitToUtf8(name);
nameProp = mitk::StringProperty::New(name);
node->SetProperty("name", nameProp);
}
else
{
// name defined in BaseData!
nameProp = mitk::StringProperty::New(baseDataNameProp->GetValue());
node->SetProperty("name", nameProp);
}
}
// visibility
if (!node->GetProperty("visible"))
{
node->SetVisibility(true);
}
}
}
diff --git a/Modules/Core/src/IO/mitkAbstractFileWriter.cpp b/Modules/Core/src/IO/mitkAbstractFileWriter.cpp
index 30220b75d7..072460a076 100644
--- a/Modules/Core/src/IO/mitkAbstractFileWriter.cpp
+++ b/Modules/Core/src/IO/mitkAbstractFileWriter.cpp
@@ -1,288 +1,289 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <mitkAbstractFileWriter.h>
#include <mitkBaseData.h>
#include <mitkCustomMimeType.h>
#include <mitkExceptionMacro.h>
#include <mitkIOUtil.h>
+#include <mitkUtf8Util.h>
#include <mitkFileReaderWriterBase.h>
#include <usGetModuleContext.h>
#include <usModuleContext.h>
#include <usPrototypeServiceFactory.h>
#include <itksys/SystemTools.hxx>
#include <fstream>
namespace mitk
{
struct AbstractFileWriter::LocalFile::Impl
{
Impl(const std::string &location, std::ostream *os) : m_Location(location), m_Stream(os) {}
std::string m_Location;
std::string m_TmpFileName;
std::ostream *m_Stream;
};
AbstractFileWriter::LocalFile::LocalFile(IFileWriter *writer)
: d(new Impl(writer->GetOutputLocation(), writer->GetOutputStream()))
{
}
AbstractFileWriter::LocalFile::~LocalFile()
{
if (d->m_Stream && !d->m_TmpFileName.empty())
{
std::ifstream ifs(d->m_TmpFileName.c_str(), std::ios_base::binary);
*d->m_Stream << ifs.rdbuf();
d->m_Stream->flush();
ifs.close();
std::remove(d->m_TmpFileName.c_str());
}
}
std::string AbstractFileWriter::LocalFile::GetFileName()
{
if (d->m_Stream == nullptr)
{
return d->m_Location;
}
else if (d->m_TmpFileName.empty())
{
- std::string ext = itksys::SystemTools::GetFilenameExtension(d->m_Location);
+ std::string ext = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetFilenameExtension(Utf8Util::Local8BitToUtf8(d->m_Location)));
d->m_TmpFileName = IOUtil::CreateTemporaryFile("XXXXXX" + ext);
}
return d->m_TmpFileName;
}
AbstractFileWriter::OutputStream::OutputStream(IFileWriter *writer, std::ios_base::openmode mode)
: std::ostream(nullptr), m_Stream(nullptr)
{
std::ostream *stream = writer->GetOutputStream();
if (stream)
{
this->init(stream->rdbuf());
}
else
{
m_Stream = new std::ofstream(writer->GetOutputLocation().c_str(), mode);
this->init(m_Stream->rdbuf());
}
}
AbstractFileWriter::OutputStream::~OutputStream() { delete m_Stream; }
class AbstractFileWriter::Impl : public FileReaderWriterBase
{
public:
Impl() : FileReaderWriterBase(), m_BaseData(nullptr), m_Stream(nullptr), m_PrototypeFactory(nullptr) {}
Impl(const Impl &other)
: FileReaderWriterBase(other),
m_BaseDataType(other.m_BaseDataType),
m_BaseData(nullptr),
m_Stream(nullptr),
m_PrototypeFactory(nullptr)
{
}
std::string m_BaseDataType;
const BaseData *m_BaseData;
std::string m_Location;
std::ostream *m_Stream;
us::PrototypeServiceFactory *m_PrototypeFactory;
us::ServiceRegistration<IFileWriter> m_Reg;
};
void AbstractFileWriter::SetInput(const BaseData *data) { d->m_BaseData = data; }
const BaseData *AbstractFileWriter::GetInput() const { return d->m_BaseData; }
void AbstractFileWriter::SetOutputLocation(const std::string &location)
{
d->m_Location = location;
d->m_Stream = nullptr;
}
std::string AbstractFileWriter::GetOutputLocation() const { return d->m_Location; }
void AbstractFileWriter::SetOutputStream(const std::string &location, std::ostream *os)
{
d->m_Location = location;
d->m_Stream = os;
}
std::ostream *AbstractFileWriter::GetOutputStream() const { return d->m_Stream; }
AbstractFileWriter::~AbstractFileWriter()
{
UnregisterService();
delete d->m_PrototypeFactory;
}
AbstractFileWriter::AbstractFileWriter(const AbstractFileWriter &other) : IFileWriter(), d(new Impl(*other.d.get()))
{
}
AbstractFileWriter::AbstractFileWriter(const std::string &baseDataType) : d(new Impl)
{
d->m_BaseDataType = baseDataType;
}
AbstractFileWriter::AbstractFileWriter(const std::string &baseDataType,
const CustomMimeType &mimeType,
const std::string &description)
: d(new Impl)
{
d->m_BaseDataType = baseDataType;
d->SetMimeType(mimeType);
d->SetDescription(description);
}
////////////////////// Writing /////////////////////////
IFileWriter::ConfidenceLevel AbstractFileWriter::GetConfidenceLevel() const
{
if (d->m_BaseData == nullptr)
return Unsupported;
std::vector<std::string> classHierarchy = d->m_BaseData->GetClassHierarchy();
if (std::find(classHierarchy.begin(), classHierarchy.end(), d->m_BaseDataType) == classHierarchy.end())
{
return Unsupported;
}
return Supported;
}
MimeType AbstractFileWriter::GetRegisteredMimeType() const { return d->GetRegisteredMimeType(); }
//////////// µS Registration & Properties //////////////
us::ServiceRegistration<IFileWriter> AbstractFileWriter::RegisterService(us::ModuleContext *context)
{
if (d->m_PrototypeFactory)
return us::ServiceRegistration<IFileWriter>();
if (context == nullptr)
{
context = us::GetModuleContext();
}
d->RegisterMimeType(context);
if (this->GetMimeType()->GetName().empty())
{
MITK_WARN << "Not registering writer due to empty MIME type.";
return us::ServiceRegistration<IFileWriter>();
}
struct PrototypeFactory : public us::PrototypeServiceFactory
{
AbstractFileWriter *const m_Prototype;
PrototypeFactory(AbstractFileWriter *prototype) : m_Prototype(prototype) {}
us::InterfaceMap GetService(us::Module * /*module*/,
const us::ServiceRegistrationBase & /*registration*/) override
{
return us::MakeInterfaceMap<IFileWriter>(m_Prototype->Clone());
}
void UngetService(us::Module * /*module*/,
const us::ServiceRegistrationBase & /*registration*/,
const us::InterfaceMap &service) override
{
delete us::ExtractInterface<IFileWriter>(service);
}
};
d->m_PrototypeFactory = new PrototypeFactory(this);
us::ServiceProperties props = this->GetServiceProperties();
d->m_Reg = context->RegisterService<IFileWriter>(d->m_PrototypeFactory, props);
return d->m_Reg;
}
void AbstractFileWriter::UnregisterService()
{
try
{
d->m_Reg.Unregister();
}
catch (const std::exception &)
{
}
}
us::ServiceProperties AbstractFileWriter::GetServiceProperties() const
{
us::ServiceProperties result;
result[IFileWriter::PROP_DESCRIPTION()] = this->GetDescription();
result[IFileWriter::PROP_MIMETYPE()] = this->GetMimeType()->GetName();
result[IFileWriter::PROP_BASEDATA_TYPE()] = d->m_BaseDataType;
result[us::ServiceConstants::SERVICE_RANKING()] = this->GetRanking();
// for (IFileWriter::OptionList::const_iterator it = d->m_Options.begin(); it != d->m_Options.end(); ++it)
// {
// result[it->first] = std::string("true");
// }
return result;
}
const CustomMimeType *AbstractFileWriter::GetMimeType() const { return d->GetMimeType(); }
void AbstractFileWriter::SetMimeTypePrefix(const std::string &prefix) { d->SetMimeTypePrefix(prefix); }
std::string AbstractFileWriter::GetMimeTypePrefix() const { return d->GetMimeTypePrefix(); }
us::ServiceRegistration<CustomMimeType> AbstractFileWriter::RegisterMimeType(us::ModuleContext *context)
{
return d->RegisterMimeType(context);
}
void AbstractFileWriter::SetMimeType(const CustomMimeType &mimeType) { d->SetMimeType(mimeType); }
void AbstractFileWriter::SetRanking(int ranking) { d->SetRanking(ranking); }
//////////////////////// Options ///////////////////////
void AbstractFileWriter::SetDefaultOptions(const IFileWriter::Options &defaultOptions)
{
d->SetDefaultOptions(defaultOptions);
}
IFileWriter::Options AbstractFileWriter::GetDefaultOptions() const { return d->GetDefaultOptions(); }
IFileWriter::Options AbstractFileWriter::GetOptions() const { return d->GetOptions(); }
us::Any AbstractFileWriter::GetOption(const std::string &name) const { return d->GetOption(name); }
void AbstractFileWriter::SetOption(const std::string &name, const us::Any &value) { d->SetOption(name, value); }
void AbstractFileWriter::SetOptions(const Options &options) { d->SetOptions(options); }
////////////////// MISC //////////////////
void AbstractFileWriter::AddProgressCallback(const ProgressCallback &callback) { d->AddProgressCallback(callback); }
void AbstractFileWriter::RemoveProgressCallback(const ProgressCallback &callback)
{
d->RemoveProgressCallback(callback);
}
////////////////// µS related Getters //////////////////
int AbstractFileWriter::GetRanking() const { return d->GetRanking(); }
void AbstractFileWriter::SetBaseDataType(const std::string &baseDataType) { d->m_BaseDataType = baseDataType; }
std::string AbstractFileWriter::GetDescription() const { return d->GetDescription(); }
std::string AbstractFileWriter::GetBaseDataType() const { return d->m_BaseDataType; }
void AbstractFileWriter::ValidateOutputLocation() const
{
if (this->GetOutputStream() == nullptr)
{
// check if a file name is set and if we can write to it
const std::string fileName = this->GetOutputLocation();
if (fileName.empty())
{
mitkThrow() << "No output location or stream specified";
}
}
}
void AbstractFileWriter::SetDescription(const std::string &description) { d->SetDescription(description); }
}
diff --git a/Modules/Core/src/IO/mitkCustomMimeType.cpp b/Modules/Core/src/IO/mitkCustomMimeType.cpp
index 30b54ef962..77b3aa0507 100644
--- a/Modules/Core/src/IO/mitkCustomMimeType.cpp
+++ b/Modules/Core/src/IO/mitkCustomMimeType.cpp
@@ -1,161 +1,163 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkCustomMimeType.h"
#include "mitkMimeType.h"
+#include <mitkUtf8Util.h>
+
#include <algorithm>
#include <itksys/SystemTools.hxx>
namespace mitk
{
class FindCaseInsensitive
{
public:
FindCaseInsensitive(std::string value)
{
lcValue.resize(value.size());
std::transform(value.begin(), value.end(), lcValue.begin(), ::tolower);
}
bool operator()(std::string elem)
{
std::transform(elem.begin(), elem.end(), elem.begin(), ::tolower);
return elem == lcValue;
}
private:
std::string lcValue;
};
struct CustomMimeType::Impl
{
std::string m_Name;
std::string m_Category;
std::vector<std::string> m_Extensions;
std::string m_Comment;
};
CustomMimeType::~CustomMimeType() { delete d; }
CustomMimeType::CustomMimeType() : d(new Impl) {}
CustomMimeType::CustomMimeType(const std::string &name) : d(new Impl) { d->m_Name = name; }
CustomMimeType::CustomMimeType(const CustomMimeType &other) : d(new Impl(*other.d)) {}
CustomMimeType::CustomMimeType(const MimeType &other) : d(new Impl)
{
d->m_Name = other.GetName();
d->m_Category = other.GetCategory();
d->m_Extensions = other.GetExtensions();
d->m_Comment = other.GetComment();
}
CustomMimeType &CustomMimeType::operator=(const CustomMimeType &other)
{
CustomMimeType tmp(other);
Swap(tmp);
return *this;
}
CustomMimeType &CustomMimeType::operator=(const MimeType &other)
{
CustomMimeType tmp(other);
Swap(tmp);
return *this;
}
std::string CustomMimeType::GetName() const { return d->m_Name; }
std::string CustomMimeType::GetCategory() const { return d->m_Category; }
std::vector<std::string> CustomMimeType::GetExtensions() const { return d->m_Extensions; }
std::string CustomMimeType::GetComment() const
{
if (!d->m_Comment.empty())
return d->m_Comment;
if (!d->m_Extensions.empty())
{
return d->m_Extensions.front() + " File";
}
return "Unknown";
}
bool CustomMimeType::AppliesTo(const std::string &path) const { return MatchesExtension(path); }
bool CustomMimeType::MatchesExtension(const std::string &path) const
{
std::string extension, filename;
return ParsePathForExtension(path, extension, filename);
}
std::string CustomMimeType::GetExtension(const std::string &path) const
{
std::string extension, filename;
ParsePathForExtension(path, extension, filename);
return extension;
}
std::string CustomMimeType::GetFilenameWithoutExtension(const std::string &path) const
{
std::string extension, filename;
ParsePathForExtension(path, extension, filename);
return filename;
}
bool CustomMimeType::ParsePathForExtension(const std::string &path,
std::string &extension,
std::string &filename) const
{
for (std::vector<std::string>::const_iterator iter = d->m_Extensions.begin(), iterEnd = d->m_Extensions.end();
iter != iterEnd;
++iter)
{
if (!iter->empty() && path.size() >= iter->size())
{
FindCaseInsensitive cmp(*iter);
if (cmp(path.substr(path.size() - iter->size())))
{
extension = "." + *iter;
- std::string filenameWithExtension = itksys::SystemTools::GetFilenameName(path);
+ std::string filenameWithExtension = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetFilenameName(Utf8Util::Local8BitToUtf8(path)));
filename = filenameWithExtension.substr(0, filenameWithExtension.size() - extension.size());
return true;
}
}
}
return false;
}
void CustomMimeType::SetName(const std::string &name) { d->m_Name = name; }
void CustomMimeType::SetCategory(const std::string &category) { d->m_Category = category; }
void CustomMimeType::SetExtension(const std::string &extension)
{
d->m_Extensions.clear();
d->m_Extensions.push_back(extension);
}
void CustomMimeType::AddExtension(const std::string &extension)
{
if (std::find_if(d->m_Extensions.begin(), d->m_Extensions.end(), FindCaseInsensitive(extension)) ==
d->m_Extensions.end())
{
d->m_Extensions.push_back(extension);
}
}
void CustomMimeType::SetComment(const std::string &comment) { d->m_Comment = comment; }
void CustomMimeType::Swap(CustomMimeType &r)
{
Impl *d1 = d;
d = r.d;
r.d = d1;
}
CustomMimeType *CustomMimeType::Clone() const { return new CustomMimeType(*this); }
void swap(CustomMimeType &l, CustomMimeType &r) { l.Swap(r); }
}
diff --git a/Modules/Core/src/IO/mitkFileReaderSelector.cpp b/Modules/Core/src/IO/mitkFileReaderSelector.cpp
index 5f7e2fb579..e4ef79a8a5 100644
--- a/Modules/Core/src/IO/mitkFileReaderSelector.cpp
+++ b/Modules/Core/src/IO/mitkFileReaderSelector.cpp
@@ -1,222 +1,223 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkFileReaderSelector.h"
#include <mitkCoreServices.h>
#include <mitkFileReaderRegistry.h>
#include <mitkIMimeTypeProvider.h>
+#include <mitkUtf8Util.h>
#include <usAny.h>
#include <usServiceProperties.h>
#include <usServiceReference.h>
#include <itksys/SystemTools.hxx>
namespace mitk
{
struct FileReaderSelector::Item::Impl : us::SharedData
{
Impl() : m_FileReader(nullptr), m_ConfidenceLevel(IFileReader::Unsupported), m_Id(-1) {}
us::ServiceReference<IFileReader> m_FileReaderRef;
IFileReader *m_FileReader;
IFileReader::ConfidenceLevel m_ConfidenceLevel;
MimeType m_MimeType;
long m_Id;
};
struct FileReaderSelector::Impl : us::SharedData
{
Impl() : m_BestId(-1), m_SelectedId(m_BestId) {}
Impl(const Impl &other) : us::SharedData(other), m_BestId(-1), m_SelectedId(m_BestId) {}
FileReaderRegistry m_ReaderRegistry;
std::map<long, FileReaderSelector::Item> m_Items;
std::vector<MimeType> m_MimeTypes;
long m_BestId;
long m_SelectedId;
};
FileReaderSelector::FileReaderSelector(const FileReaderSelector &other) : m_Data(other.m_Data) {}
FileReaderSelector::FileReaderSelector(const std::string &path) : m_Data(new Impl)
{
- if (!itksys::SystemTools::FileExists(path.c_str()))
+ if (!itksys::SystemTools::FileExists(Utf8Util::Local8BitToUtf8(path).c_str()))
{
return;
}
mitk::CoreServicePointer<mitk::IMimeTypeProvider> mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider());
// Get all mime types and associated readers for the given file path
m_Data->m_MimeTypes = mimeTypeProvider->GetMimeTypesForFile(path);
if (m_Data->m_MimeTypes.empty())
return;
for (std::vector<MimeType>::const_iterator mimeTypeIter = m_Data->m_MimeTypes.begin(),
mimeTypeIterEnd = m_Data->m_MimeTypes.end();
mimeTypeIter != mimeTypeIterEnd;
++mimeTypeIter)
{
std::vector<FileReaderRegistry::ReaderReference> refs = m_Data->m_ReaderRegistry.GetReferences(*mimeTypeIter);
for (std::vector<FileReaderRegistry::ReaderReference>::const_iterator readerIter = refs.begin(),
iterEnd = refs.end();
readerIter != iterEnd;
++readerIter)
{
IFileReader *reader = m_Data->m_ReaderRegistry.GetReader(*readerIter);
if (reader == nullptr)
continue;
try
{
reader->SetInput(path);
IFileReader::ConfidenceLevel confidenceLevel = reader->GetConfidenceLevel();
if (confidenceLevel == IFileReader::Unsupported)
{
continue;
}
Item item;
item.d->m_FileReaderRef = *readerIter;
item.d->m_FileReader = reader;
item.d->m_ConfidenceLevel = confidenceLevel;
item.d->m_MimeType = *mimeTypeIter;
item.d->m_Id = us::any_cast<long>(readerIter->GetProperty(us::ServiceConstants::SERVICE_ID()));
m_Data->m_Items.insert(std::make_pair(item.d->m_Id, item));
// m_Data->m_MimeTypes.insert(mimeType);
}
catch (const us::BadAnyCastException &e)
{
MITK_WARN << "Unexpected: " << e.what();
}
catch (const std::exception &e)
{
// Log the error but continue
MITK_WARN << "IFileWriter::GetConfidenceLevel exception: " << e.what();
}
}
}
// get the "best" reader
if (!m_Data->m_Items.empty())
{
std::set<Item> sortedItems;
for (std::map<long, Item>::const_iterator iter = m_Data->m_Items.begin(), iterEnd = m_Data->m_Items.end();
iter != iterEnd;
++iter)
{
sortedItems.insert(iter->second);
}
m_Data->m_BestId = sortedItems.rbegin()->GetServiceId();
m_Data->m_SelectedId = m_Data->m_BestId;
}
}
FileReaderSelector::~FileReaderSelector() {}
FileReaderSelector &FileReaderSelector::operator=(const FileReaderSelector &other)
{
m_Data = other.m_Data;
return *this;
}
bool FileReaderSelector::IsEmpty() const { return m_Data->m_Items.empty(); }
std::vector<MimeType> FileReaderSelector::GetMimeTypes() const { return m_Data->m_MimeTypes; }
std::vector<FileReaderSelector::Item> FileReaderSelector::Get() const
{
std::vector<Item> result;
for (std::map<long, Item>::const_iterator iter = m_Data->m_Items.begin(), iterEnd = m_Data->m_Items.end();
iter != iterEnd;
++iter)
{
result.push_back(iter->second);
}
std::sort(result.begin(), result.end());
return result;
}
FileReaderSelector::Item FileReaderSelector::Get(long id) const
{
std::map<long, Item>::const_iterator iter = m_Data->m_Items.find(id);
if (iter != m_Data->m_Items.end())
{
return iter->second;
}
return Item();
}
FileReaderSelector::Item FileReaderSelector::GetDefault() const { return Get(m_Data->m_BestId); }
long FileReaderSelector::GetDefaultId() const { return m_Data->m_BestId; }
FileReaderSelector::Item FileReaderSelector::GetSelected() const { return Get(m_Data->m_SelectedId); }
long FileReaderSelector::GetSelectedId() const { return m_Data->m_SelectedId; }
bool FileReaderSelector::Select(const FileReaderSelector::Item &item) { return Select(item.d->m_Id); }
bool FileReaderSelector::Select(long id)
{
if (id > -1)
{
if (m_Data->m_Items.find(id) == m_Data->m_Items.end())
{
return false;
}
m_Data->m_SelectedId = id;
return true;
}
return false;
}
void FileReaderSelector::Swap(FileReaderSelector &fws) { m_Data.Swap(fws.m_Data); }
FileReaderSelector::Item::Item(const FileReaderSelector::Item &other) : d(other.d) {}
FileReaderSelector::Item::~Item() {}
FileReaderSelector::Item &FileReaderSelector::Item::operator=(const FileReaderSelector::Item &other)
{
d = other.d;
return *this;
}
IFileReader *FileReaderSelector::Item::GetReader() const { return d->m_FileReader; }
std::string FileReaderSelector::Item::GetDescription() const
{
us::Any descr = d->m_FileReaderRef.GetProperty(IFileReader::PROP_DESCRIPTION());
if (descr.Empty())
return std::string();
return descr.ToString();
}
IFileReader::ConfidenceLevel FileReaderSelector::Item::GetConfidenceLevel() const { return d->m_ConfidenceLevel; }
MimeType FileReaderSelector::Item::GetMimeType() const { return d->m_MimeType; }
us::ServiceReference<IFileReader> FileReaderSelector::Item::GetReference() const { return d->m_FileReaderRef; }
long FileReaderSelector::Item::GetServiceId() const { return d->m_Id; }
bool FileReaderSelector::Item::operator<(const FileReaderSelector::Item &other) const
{
// sort by confidence level first (ascending)
if (d->m_ConfidenceLevel == other.d->m_ConfidenceLevel)
{
// sort by mime-type ranking
if (d->m_MimeType < other.d->m_MimeType)
{
return true;
}
else if (other.d->m_MimeType < d->m_MimeType)
{
return false;
}
else
{
// sort by file writer service ranking
return d->m_FileReaderRef < other.d->m_FileReaderRef;
}
}
return d->m_ConfidenceLevel < other.d->m_ConfidenceLevel;
}
FileReaderSelector::Item::Item() : d(new Impl()) {}
void swap(FileReaderSelector &frs1, FileReaderSelector &frs2) { frs1.Swap(frs2); }
}
diff --git a/Modules/Core/src/IO/mitkFileWriterSelector.cpp b/Modules/Core/src/IO/mitkFileWriterSelector.cpp
index dbc14e6be4..116d6a2596 100644
--- a/Modules/Core/src/IO/mitkFileWriterSelector.cpp
+++ b/Modules/Core/src/IO/mitkFileWriterSelector.cpp
@@ -1,290 +1,291 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkFileWriterSelector.h"
#include <mitkBaseData.h>
#include <mitkCoreServices.h>
#include <mitkFileWriterRegistry.h>
#include <mitkIMimeTypeProvider.h>
+#include <mitkUtf8Util.h>
#include <usAny.h>
#include <usServiceProperties.h>
#include <usServiceReference.h>
#include <itksys/SystemTools.hxx>
#include <iterator>
#include <limits>
#include <set>
namespace mitk
{
struct FileWriterSelector::Item::Impl : us::SharedData
{
Impl() : m_FileWriter(nullptr), m_ConfidenceLevel(IFileWriter::Unsupported), m_BaseDataIndex(0), m_Id(-1) {}
us::ServiceReference<IFileWriter> m_FileWriterRef;
IFileWriter *m_FileWriter;
IFileWriter::ConfidenceLevel m_ConfidenceLevel;
std::size_t m_BaseDataIndex;
MimeType m_MimeType;
long m_Id;
};
struct FileWriterSelector::Impl : us::SharedData
{
Impl() : m_BestId(-1), m_SelectedId(m_BestId) {}
Impl(const Impl &other) : us::SharedData(other), m_BestId(-1), m_SelectedId(m_BestId) {}
FileWriterRegistry m_WriterRegistry;
std::map<long, FileWriterSelector::Item> m_Items;
std::set<MimeType> m_MimeTypes;
long m_BestId;
long m_SelectedId;
};
FileWriterSelector::FileWriterSelector(const FileWriterSelector &other) : m_Data(other.m_Data) {}
FileWriterSelector::FileWriterSelector(const BaseData *baseData, const std::string &mimeType, const std::string &path)
: m_Data(new Impl)
{
mitk::CoreServicePointer<mitk::IMimeTypeProvider> mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider());
std::vector<FileWriterRegistry::WriterReference> refs;
std::string destMimeType = mimeType;
if (destMimeType.empty() && !path.empty())
{
// try to derive a mime-type from the file
std::vector<MimeType> mimeTypes = mimeTypeProvider->GetMimeTypesForFile(path);
if (!mimeTypes.empty())
{
for (unsigned int index = 0; index < mimeTypes.size(); index++)
{
std::vector<FileWriterRegistry::WriterReference> tempRefs =
m_Data->m_WriterRegistry.GetReferences(baseData, mimeTypes.at(index).GetName());
for (unsigned int innerIndex = 0; innerIndex < tempRefs.size(); innerIndex++)
{
refs.push_back(tempRefs.at(innerIndex));
}
}
}
- else if (!itksys::SystemTools::GetFilenameExtension(path).empty())
+ else if (!itksys::SystemTools::GetFilenameExtension(Utf8Util::Local8BitToUtf8(path)).empty())
{
// If there are no suitable mime-type for the file AND an extension
// was supplied, we stop here.
return;
}
else
{
refs = m_Data->m_WriterRegistry.GetReferences(baseData, destMimeType);
}
}
else
{
refs = m_Data->m_WriterRegistry.GetReferences(baseData, destMimeType);
}
std::vector<std::string> classHierarchy = baseData->GetClassHierarchy();
// Get all writers and their mime types for the given base data type
Item bestItem;
for (std::vector<FileWriterRegistry::WriterReference>::const_iterator iter = refs.begin(), iterEnd = refs.end();
iter != iterEnd;
++iter)
{
std::string mimeTypeName = iter->GetProperty(IFileWriter::PROP_MIMETYPE()).ToString();
if (!mimeTypeName.empty())
{
MimeType mimeType = mimeTypeProvider->GetMimeTypeForName(mimeTypeName);
if (mimeType.IsValid())
{
// There is a registered mime-type for this writer. Now get the confidence level
// of this writer for writing the given base data object.
IFileWriter *writer = m_Data->m_WriterRegistry.GetWriter(*iter);
if (writer == nullptr)
continue;
try
{
writer->SetInput(baseData);
IFileWriter::ConfidenceLevel confidenceLevel = writer->GetConfidenceLevel();
if (confidenceLevel == IFileWriter::Unsupported)
{
continue;
}
std::string baseDataType = iter->GetProperty(IFileWriter::PROP_BASEDATA_TYPE()).ToString();
auto idxIter =
std::find(classHierarchy.begin(), classHierarchy.end(), baseDataType);
std::size_t baseDataIndex = std::numeric_limits<std::size_t>::max();
if (idxIter != classHierarchy.end())
{
baseDataIndex = std::distance(classHierarchy.begin(), idxIter);
}
Item item;
item.d->m_FileWriterRef = *iter;
item.d->m_FileWriter = writer;
item.d->m_ConfidenceLevel = confidenceLevel;
item.d->m_BaseDataIndex = baseDataIndex;
item.d->m_MimeType = mimeType;
item.d->m_Id = us::any_cast<long>(iter->GetProperty(us::ServiceConstants::SERVICE_ID()));
m_Data->m_Items.insert(std::make_pair(item.d->m_Id, item));
m_Data->m_MimeTypes.insert(mimeType);
if (!bestItem.GetReference() || bestItem < item)
{
bestItem = item;
}
}
catch (const us::BadAnyCastException &e)
{
MITK_WARN << "Unexpected: " << e.what();
}
catch (const std::exception &e)
{
// Log the error but continue
MITK_WARN << "IFileWriter::GetConfidenceLevel exception: " << e.what();
}
}
}
}
if (bestItem.GetReference())
{
m_Data->m_BestId = bestItem.GetServiceId();
m_Data->m_SelectedId = m_Data->m_BestId;
}
}
FileWriterSelector::~FileWriterSelector() {}
FileWriterSelector &FileWriterSelector::operator=(const FileWriterSelector &other)
{
m_Data = other.m_Data;
return *this;
}
bool FileWriterSelector::IsEmpty() const { return m_Data->m_Items.empty(); }
std::vector<FileWriterSelector::Item> FileWriterSelector::Get(const std::string &mimeType) const
{
std::vector<Item> result;
for (std::map<long, Item>::const_iterator iter = m_Data->m_Items.begin(), iterEnd = m_Data->m_Items.end();
iter != iterEnd;
++iter)
{
if (mimeType.empty() || iter->second.GetMimeType().GetName() == mimeType)
{
result.push_back(iter->second);
}
}
std::sort(result.begin(), result.end());
return result;
}
std::vector<FileWriterSelector::Item> FileWriterSelector::Get() const
{
return Get(this->GetSelected().d->m_MimeType.GetName());
}
FileWriterSelector::Item FileWriterSelector::Get(long id) const
{
std::map<long, Item>::const_iterator iter = m_Data->m_Items.find(id);
if (iter != m_Data->m_Items.end())
{
return iter->second;
}
return Item();
}
FileWriterSelector::Item FileWriterSelector::GetDefault() const { return Get(m_Data->m_BestId); }
long FileWriterSelector::GetDefaultId() const { return m_Data->m_BestId; }
FileWriterSelector::Item FileWriterSelector::GetSelected() const { return Get(m_Data->m_SelectedId); }
long FileWriterSelector::GetSelectedId() const { return m_Data->m_SelectedId; }
bool FileWriterSelector::Select(const std::string &mimeType)
{
std::vector<Item> items = Get(mimeType);
if (items.empty())
return false;
return Select(items.back());
}
bool FileWriterSelector::Select(const FileWriterSelector::Item &item) { return Select(item.d->m_Id); }
bool FileWriterSelector::Select(long id)
{
if (id > -1)
{
if (m_Data->m_Items.find(id) == m_Data->m_Items.end())
{
return false;
}
m_Data->m_SelectedId = id;
return true;
}
return false;
}
std::vector<MimeType> FileWriterSelector::GetMimeTypes() const
{
std::vector<MimeType> result;
result.reserve(m_Data->m_MimeTypes.size());
result.assign(m_Data->m_MimeTypes.begin(), m_Data->m_MimeTypes.end());
return result;
}
void FileWriterSelector::Swap(FileWriterSelector &fws) { m_Data.Swap(fws.m_Data); }
FileWriterSelector::Item::Item(const FileWriterSelector::Item &other) : d(other.d) {}
FileWriterSelector::Item::~Item() {}
FileWriterSelector::Item &FileWriterSelector::Item::operator=(const FileWriterSelector::Item &other)
{
d = other.d;
return *this;
}
IFileWriter *FileWriterSelector::Item::GetWriter() const { return d->m_FileWriter; }
std::string FileWriterSelector::Item::GetDescription() const
{
us::Any descr = d->m_FileWriterRef.GetProperty(IFileWriter::PROP_DESCRIPTION());
if (descr.Empty())
return std::string();
return descr.ToString();
}
IFileWriter::ConfidenceLevel FileWriterSelector::Item::GetConfidenceLevel() const { return d->m_ConfidenceLevel; }
MimeType FileWriterSelector::Item::GetMimeType() const { return d->m_MimeType; }
std::string FileWriterSelector::Item::GetBaseDataType() const
{
us::Any any = d->m_FileWriterRef.GetProperty(IFileWriter::PROP_BASEDATA_TYPE());
if (any.Empty())
return std::string();
return any.ToString();
}
us::ServiceReference<IFileWriter> FileWriterSelector::Item::GetReference() const { return d->m_FileWriterRef; }
long FileWriterSelector::Item::GetServiceId() const { return d->m_Id; }
bool FileWriterSelector::Item::operator<(const FileWriterSelector::Item &other) const
{
// sort by confidence level first (ascending)
if (d->m_ConfidenceLevel == other.d->m_ConfidenceLevel)
{
// sort by class hierarchy index (writers for more derived
// based data types are considered a better match)
if (d->m_BaseDataIndex == other.d->m_BaseDataIndex)
{
// sort by file writer service ranking
return d->m_FileWriterRef < other.d->m_FileWriterRef;
}
return other.d->m_BaseDataIndex < d->m_BaseDataIndex;
}
return d->m_ConfidenceLevel < other.d->m_ConfidenceLevel;
}
FileWriterSelector::Item::Item() : d(new Impl()) {}
void swap(FileWriterSelector &fws1, FileWriterSelector &fws2) { fws1.Swap(fws2); }
}
diff --git a/Modules/Core/src/IO/mitkIOMimeTypes.cpp b/Modules/Core/src/IO/mitkIOMimeTypes.cpp
index 34c558825a..0dd2af91a8 100644
--- a/Modules/Core/src/IO/mitkIOMimeTypes.cpp
+++ b/Modules/Core/src/IO/mitkIOMimeTypes.cpp
@@ -1,363 +1,365 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkIOMimeTypes.h"
#include "mitkCustomMimeType.h"
#include "mitkLogMacros.h"
+#include <mitkUtf8Util.h>
#include "itkGDCMImageIO.h"
#include "itkMetaDataObject.h"
#include <itksys/SystemTools.hxx>
#include <itksys/Directory.hxx>
namespace mitk
{
IOMimeTypes::BaseDicomMimeType::BaseDicomMimeType(const std::string& name) : CustomMimeType(name)
{
this->AddExtension("gdcm");
this->AddExtension("dcm");
this->AddExtension("DCM");
this->AddExtension("dc3");
this->AddExtension("DC3");
this->AddExtension("ima");
this->AddExtension("img");
this->SetCategory(CATEGORY_IMAGES());
this->SetComment("DICOM");
}
bool IOMimeTypes::BaseDicomMimeType::AppliesTo(const std::string &path) const
{
// check whether directory or file
// if directory try to find first file within it instead
- bool pathIsDirectory = itksys::SystemTools::FileIsDirectory(path);
+ bool pathIsDirectory = itksys::SystemTools::FileIsDirectory(Utf8Util::Local8BitToUtf8(path));
std::string filepath = path;
if (pathIsDirectory)
{
itksys::Directory input;
input.Load(path.c_str());
std::vector<std::string> files;
for (unsigned long idx = 0; idx<input.GetNumberOfFiles(); idx++)
{
- auto filename = input.GetFile(idx);
+ auto filename = Utf8Util::Local8BitToUtf8(input.GetFile(idx));
+
if (!itksys::SystemTools::FileIsDirectory(filename) && this->MatchesExtension(filename))
{
std::string fullpath = path + "/" + std::string(input.GetFile(idx));
files.push_back(fullpath.c_str());
}
}
if (!files.empty())
{
filepath = files.front();
}
}
// Ask the GDCM ImageIO class directly
itk::GDCMImageIO::Pointer gdcmIO = itk::GDCMImageIO::New();
gdcmIO->SetFileName(filepath);
try {
gdcmIO->ReadImageInformation();
}
catch (const itk::ExceptionObject & /*err*/) {
return false;
}
//DICOMRT modalities have specific reader, don't read with normal DICOM readers
std::string modality;
itk::MetaDataDictionary& dict = gdcmIO->GetMetaDataDictionary();
itk::ExposeMetaData<std::string>(dict, "0008|0060", modality);
MITK_DEBUG << "DICOM Modality detected by MimeType "<< this->GetName() << " is " << modality;
if (modality == "RTSTRUCT" || modality == "RTDOSE" || modality == "RTPLAN") {
return false;
}
else {
return gdcmIO->CanReadFile(filepath.c_str());
}
}
IOMimeTypes::BaseDicomMimeType*IOMimeTypes::BaseDicomMimeType::Clone() const { return new BaseDicomMimeType(*this); }
IOMimeTypes::DicomMimeType::DicomMimeType() : BaseDicomMimeType(DICOM_MIMETYPE_NAME())
{
}
IOMimeTypes::DicomMimeType* IOMimeTypes::DicomMimeType::Clone() const { return new DicomMimeType(*this); }
std::vector<CustomMimeType *> IOMimeTypes::Get()
{
std::vector<CustomMimeType *> mimeTypes;
// order matters here (descending rank for mime types)
mimeTypes.push_back(NRRD_MIMETYPE().Clone());
mimeTypes.push_back(NIFTI_MIMETYPE().Clone());
mimeTypes.push_back(VTK_IMAGE_MIMETYPE().Clone());
mimeTypes.push_back(VTK_PARALLEL_IMAGE_MIMETYPE().Clone());
mimeTypes.push_back(VTK_IMAGE_LEGACY_MIMETYPE().Clone());
mimeTypes.push_back(DICOM_MIMETYPE().Clone());
mimeTypes.push_back(VTK_POLYDATA_MIMETYPE().Clone());
mimeTypes.push_back(VTK_PARALLEL_POLYDATA_MIMETYPE().Clone());
mimeTypes.push_back(VTK_POLYDATA_LEGACY_MIMETYPE().Clone());
mimeTypes.push_back(STEREOLITHOGRAPHY_MIMETYPE().Clone());
mimeTypes.push_back(WAVEFRONT_OBJ_MIMETYPE().Clone());
mimeTypes.push_back(STANFORD_PLY_MIMETYPE().Clone());
mimeTypes.push_back(RAW_MIMETYPE().Clone());
mimeTypes.push_back(POINTSET_MIMETYPE().Clone());
return mimeTypes;
}
CustomMimeType IOMimeTypes::VTK_IMAGE_MIMETYPE()
{
CustomMimeType mimeType(VTK_IMAGE_NAME());
mimeType.AddExtension("vti");
mimeType.SetCategory(CATEGORY_IMAGES());
mimeType.SetComment("VTK Image");
return mimeType;
}
CustomMimeType IOMimeTypes::VTK_IMAGE_LEGACY_MIMETYPE()
{
CustomMimeType mimeType(VTK_IMAGE_LEGACY_NAME());
mimeType.AddExtension("vtk");
mimeType.SetCategory(CATEGORY_IMAGES());
mimeType.SetComment("VTK Legacy Image");
return mimeType;
}
CustomMimeType IOMimeTypes::VTK_PARALLEL_IMAGE_MIMETYPE()
{
CustomMimeType mimeType(VTK_PARALLEL_IMAGE_NAME());
mimeType.AddExtension("pvti");
mimeType.SetCategory(CATEGORY_IMAGES());
mimeType.SetComment("VTK Parallel Image");
return mimeType;
}
CustomMimeType IOMimeTypes::VTK_POLYDATA_MIMETYPE()
{
CustomMimeType mimeType(VTK_POLYDATA_NAME());
mimeType.AddExtension("vtp");
mimeType.SetCategory(CATEGORY_SURFACES());
mimeType.SetComment("VTK PolyData");
return mimeType;
}
CustomMimeType IOMimeTypes::VTK_POLYDATA_LEGACY_MIMETYPE()
{
CustomMimeType mimeType(VTK_POLYDATA_LEGACY_NAME());
mimeType.AddExtension("vtk");
mimeType.SetCategory(CATEGORY_SURFACES());
mimeType.SetComment("VTK Legacy PolyData");
return mimeType;
}
CustomMimeType IOMimeTypes::VTK_PARALLEL_POLYDATA_MIMETYPE()
{
CustomMimeType mimeType(VTK_PARALLEL_POLYDATA_NAME());
mimeType.AddExtension("pvtp");
mimeType.SetCategory(CATEGORY_SURFACES());
mimeType.SetComment("VTK Parallel PolyData");
return mimeType;
}
CustomMimeType IOMimeTypes::STEREOLITHOGRAPHY_MIMETYPE()
{
CustomMimeType mimeType(STEREOLITHOGRAPHY_NAME());
mimeType.AddExtension("stl");
mimeType.SetCategory(CATEGORY_SURFACES());
mimeType.SetComment("Stereolithography");
return mimeType;
}
CustomMimeType IOMimeTypes::WAVEFRONT_OBJ_MIMETYPE()
{
CustomMimeType mimeType(WAVEFRONT_OBJ_NAME());
mimeType.AddExtension("obj");
mimeType.SetCategory(CATEGORY_SURFACES());
mimeType.SetComment("Wavefront OBJ");
return mimeType;
}
CustomMimeType IOMimeTypes::STANFORD_PLY_MIMETYPE()
{
CustomMimeType mimeType(STANFORD_PLY_NAME());
mimeType.AddExtension("ply");
mimeType.SetCategory(CATEGORY_SURFACES());
mimeType.SetComment("Stanford PLY");
return mimeType;
}
std::string IOMimeTypes::STEREOLITHOGRAPHY_NAME()
{
static std::string name = DEFAULT_BASE_NAME() + ".stl";
return name;
}
std::string IOMimeTypes::WAVEFRONT_OBJ_NAME()
{
static std::string name = DEFAULT_BASE_NAME() + ".obj";
return name;
}
std::string IOMimeTypes::STANFORD_PLY_NAME()
{
static std::string name = DEFAULT_BASE_NAME() + ".ply";
return name;
}
std::string IOMimeTypes::DEFAULT_BASE_NAME()
{
static std::string name = "application/vnd.mitk";
return name;
}
std::string IOMimeTypes::CATEGORY_IMAGES()
{
static std::string cat = "Images";
return cat;
}
std::string IOMimeTypes::CATEGORY_SURFACES()
{
static std::string cat = "Surfaces";
return cat;
}
std::string IOMimeTypes::VTK_IMAGE_NAME()
{
static std::string name = DEFAULT_BASE_NAME() + ".vtk.image";
return name;
}
std::string IOMimeTypes::VTK_IMAGE_LEGACY_NAME()
{
static std::string name = DEFAULT_BASE_NAME() + ".vtk.image.legacy";
return name;
}
std::string IOMimeTypes::VTK_PARALLEL_IMAGE_NAME()
{
static std::string name = DEFAULT_BASE_NAME() + ".vtk.parallel.image";
return name;
}
std::string IOMimeTypes::VTK_POLYDATA_NAME()
{
static std::string name = DEFAULT_BASE_NAME() + ".vtk.polydata";
return name;
}
std::string IOMimeTypes::VTK_POLYDATA_LEGACY_NAME()
{
static std::string name = DEFAULT_BASE_NAME() + ".vtk.polydata.legacy";
return name;
}
std::string IOMimeTypes::VTK_PARALLEL_POLYDATA_NAME()
{
static std::string name = DEFAULT_BASE_NAME() + ".vtk.parallel.polydata";
return name;
}
CustomMimeType IOMimeTypes::NRRD_MIMETYPE()
{
CustomMimeType mimeType(NRRD_MIMETYPE_NAME());
mimeType.AddExtension("nrrd");
mimeType.AddExtension("nhdr");
mimeType.SetCategory("Images");
mimeType.SetComment("NRRD");
return mimeType;
}
CustomMimeType IOMimeTypes::NIFTI_MIMETYPE()
{
CustomMimeType mimeType(NIFTI_MIMETYPE_NAME());
mimeType.AddExtension("nii");
mimeType.AddExtension("nii.gz");
mimeType.AddExtension("hdr");
mimeType.AddExtension("hdr.gz");
mimeType.AddExtension("img");
mimeType.AddExtension("img.gz");
mimeType.AddExtension("nia");
mimeType.SetCategory("Images");
mimeType.SetComment("Nifti");
return mimeType;
}
CustomMimeType IOMimeTypes::RAW_MIMETYPE()
{
CustomMimeType mimeType(RAW_MIMETYPE_NAME());
mimeType.AddExtension("raw");
mimeType.SetCategory("Images");
mimeType.SetComment("Raw data");
return mimeType;
}
IOMimeTypes::DicomMimeType IOMimeTypes::DICOM_MIMETYPE() { return DicomMimeType(); }
std::string IOMimeTypes::NRRD_MIMETYPE_NAME()
{
static std::string name = DEFAULT_BASE_NAME() + ".image.nrrd";
return name;
}
std::string IOMimeTypes::NIFTI_MIMETYPE_NAME()
{
static std::string name = DEFAULT_BASE_NAME() + ".image.nifti";
return name;
}
std::string IOMimeTypes::RAW_MIMETYPE_NAME()
{
static std::string name = DEFAULT_BASE_NAME() + ".image.raw";
return name;
}
std::string IOMimeTypes::DICOM_MIMETYPE_NAME()
{
static std::string name = DEFAULT_BASE_NAME() + ".image.dicom";
return name;
}
CustomMimeType IOMimeTypes::POINTSET_MIMETYPE()
{
CustomMimeType mimeType(POINTSET_MIMETYPE_NAME());
mimeType.AddExtension("mps");
mimeType.SetCategory("Point Sets");
mimeType.SetComment("MITK Point Set");
return mimeType;
}
std::string IOMimeTypes::POINTSET_MIMETYPE_NAME()
{
static std::string name = DEFAULT_BASE_NAME() + ".pointset";
return name;
}
CustomMimeType IOMimeTypes::GEOMETRY_DATA_MIMETYPE()
{
mitk::CustomMimeType mimeType(DEFAULT_BASE_NAME() + ".geometrydata");
mimeType.AddExtension("mitkgeometry");
mimeType.SetCategory("Geometries");
mimeType.SetComment("GeometryData object");
return mimeType;
}
}
diff --git a/Modules/Core/src/IO/mitkIOUtil.cpp b/Modules/Core/src/IO/mitkIOUtil.cpp
index 9089e4bd69..5cf1ecba84 100644
--- a/Modules/Core/src/IO/mitkIOUtil.cpp
+++ b/Modules/Core/src/IO/mitkIOUtil.cpp
@@ -1,1063 +1,991 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkIOUtil.h"
#include <mitkCoreObjectFactory.h>
#include <mitkCoreServices.h>
#include <mitkExceptionMacro.h>
#include <mitkFileReaderRegistry.h>
#include <mitkFileWriterRegistry.h>
#include <mitkIMimeTypeProvider.h>
#include <mitkProgressBar.h>
#include <mitkStandaloneDataStorage.h>
#include <usGetModuleContext.h>
#include <usLDAPProp.h>
#include <usModuleContext.h>
#include <usModuleResource.h>
#include <usModuleResourceStream.h>
#include <mitkAbstractFileReader.h>
+#include <mitkUtf8Util.h>
// ITK
#include <itksys/SystemTools.hxx>
// VTK
#include <vtkPolyData.h>
#include <vtkSmartPointer.h>
#include <vtkTriangleFilter.h>
#include <cerrno>
#include <cstdlib>
-#ifdef US_PLATFORM_WINDOWS
-
-#include <Windows.h>
-
-namespace
-{
- std::wstring MultiByteToWideChar(const std::string& mbString, UINT codePage)
- {
- auto numChars = ::MultiByteToWideChar(codePage, 0, mbString.data(), mbString.size(), nullptr, 0);
-
- if (0 >= numChars)
- mitkThrow() << "Failure to convert multi-byte character string to wide character string";
-
- std::wstring wString;
- wString.resize(numChars);
-
- ::MultiByteToWideChar(codePage, 0, mbString.data(), mbString.size(), &wString[0], static_cast<int>(wString.size()));
-
- return wString;
- }
-
- std::string WideCharToMultiByte(const std::wstring& wString, UINT codePage)
- {
- auto numChars = ::WideCharToMultiByte(codePage, 0, wString.data(), wString.size(), nullptr, 0, nullptr, nullptr);
-
- if (0 >= numChars)
- mitkThrow() << "Failure to convert wide character string to multi-byte character string";
-
- std::string mbString;
- mbString.resize(numChars);
-
- ::WideCharToMultiByte(codePage, 0, wString.data(), wString.size(), &mbString[0], static_cast<int>(mbString.size()), nullptr, nullptr);
-
- return mbString;
- }
-}
-
-#endif
-
static std::string GetLastErrorStr()
{
#ifdef US_PLATFORM_POSIX
return std::string(strerror(errno));
#else
// Retrieve the system error message for the last-error code
LPVOID lpMsgBuf;
DWORD dw = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0,
nullptr);
std::string errMsg((LPCTSTR)lpMsgBuf);
LocalFree(lpMsgBuf);
return errMsg;
#endif
}
#ifdef US_PLATFORM_WINDOWS
#include <direct.h>
#include <io.h>
// make the posix flags point to the obsolte bsd types on windows
#define S_IRUSR S_IREAD
#define S_IWUSR S_IWRITE
#else
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#endif
#include <fcntl.h>
#include <sys/stat.h>
static const char validLetters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
// A cross-platform version of the mkstemps function
static int mkstemps_compat(char *tmpl, int suffixlen)
{
static unsigned long long value = 0;
int savedErrno = errno;
// Lower bound on the number of temporary files to attempt to generate.
#define ATTEMPTS_MIN (62 * 62 * 62)
/* The number of times to attempt to generate a temporary file. To
conform to POSIX, this must be no smaller than TMP_MAX. */
#if ATTEMPTS_MIN < TMP_MAX
const unsigned int attempts = TMP_MAX;
#else
const unsigned int attempts = ATTEMPTS_MIN;
#endif
const int len = strlen(tmpl);
if ((len - suffixlen) < 6 || strncmp(&tmpl[len - 6 - suffixlen], "XXXXXX", 6))
{
errno = EINVAL;
return -1;
}
/* This is where the Xs start. */
char *XXXXXX = &tmpl[len - 6 - suffixlen];
/* Get some more or less random data. */
#ifdef US_PLATFORM_WINDOWS
{
SYSTEMTIME stNow;
FILETIME ftNow;
// get system time
GetSystemTime(&stNow);
stNow.wMilliseconds = 500;
if (!SystemTimeToFileTime(&stNow, &ftNow))
{
errno = -1;
return -1;
}
unsigned long long randomTimeBits = ((static_cast<unsigned long long>(ftNow.dwHighDateTime) << 32) |
static_cast<unsigned long long>(ftNow.dwLowDateTime));
value = randomTimeBits ^ static_cast<unsigned long long>(GetCurrentThreadId());
}
#else
{
struct timeval tv;
gettimeofday(&tv, nullptr);
unsigned long long randomTimeBits =
((static_cast<unsigned long long>(tv.tv_usec) << 32) | static_cast<unsigned long long>(tv.tv_sec));
value = randomTimeBits ^ static_cast<unsigned long long>(getpid());
}
#endif
for (unsigned int count = 0; count < attempts; value += 7777, ++count)
{
unsigned long long v = value;
/* Fill in the random bits. */
XXXXXX[0] = validLetters[v % 62];
v /= 62;
XXXXXX[1] = validLetters[v % 62];
v /= 62;
XXXXXX[2] = validLetters[v % 62];
v /= 62;
XXXXXX[3] = validLetters[v % 62];
v /= 62;
XXXXXX[4] = validLetters[v % 62];
v /= 62;
XXXXXX[5] = validLetters[v % 62];
int fd = open(tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
if (fd >= 0)
{
errno = savedErrno;
return fd;
}
else if (errno != EEXIST)
{
return -1;
}
}
/* We got out of the loop because we ran out of combinations to try. */
errno = EEXIST;
return -1;
}
// A cross-platform version of the POSIX mkdtemp function
static char *mkdtemps_compat(char *tmpl, int suffixlen)
{
static unsigned long long value = 0;
int savedErrno = errno;
// Lower bound on the number of temporary dirs to attempt to generate.
#define ATTEMPTS_MIN (62 * 62 * 62)
/* The number of times to attempt to generate a temporary dir. To
conform to POSIX, this must be no smaller than TMP_MAX. */
#if ATTEMPTS_MIN < TMP_MAX
const unsigned int attempts = TMP_MAX;
#else
const unsigned int attempts = ATTEMPTS_MIN;
#endif
const int len = strlen(tmpl);
if ((len - suffixlen) < 6 || strncmp(&tmpl[len - 6 - suffixlen], "XXXXXX", 6))
{
errno = EINVAL;
return nullptr;
}
/* This is where the Xs start. */
char *XXXXXX = &tmpl[len - 6 - suffixlen];
/* Get some more or less random data. */
#ifdef US_PLATFORM_WINDOWS
{
SYSTEMTIME stNow;
FILETIME ftNow;
// get system time
GetSystemTime(&stNow);
stNow.wMilliseconds = 500;
if (!SystemTimeToFileTime(&stNow, &ftNow))
{
errno = -1;
return nullptr;
}
unsigned long long randomTimeBits = ((static_cast<unsigned long long>(ftNow.dwHighDateTime) << 32) |
static_cast<unsigned long long>(ftNow.dwLowDateTime));
value = randomTimeBits ^ static_cast<unsigned long long>(GetCurrentThreadId());
}
#else
{
struct timeval tv;
gettimeofday(&tv, nullptr);
unsigned long long randomTimeBits =
((static_cast<unsigned long long>(tv.tv_usec) << 32) | static_cast<unsigned long long>(tv.tv_sec));
value = randomTimeBits ^ static_cast<unsigned long long>(getpid());
}
#endif
unsigned int count = 0;
for (; count < attempts; value += 7777, ++count)
{
unsigned long long v = value;
/* Fill in the random bits. */
XXXXXX[0] = validLetters[v % 62];
v /= 62;
XXXXXX[1] = validLetters[v % 62];
v /= 62;
XXXXXX[2] = validLetters[v % 62];
v /= 62;
XXXXXX[3] = validLetters[v % 62];
v /= 62;
XXXXXX[4] = validLetters[v % 62];
v /= 62;
XXXXXX[5] = validLetters[v % 62];
#ifdef US_PLATFORM_WINDOWS
int fd = _mkdir(tmpl); //, _S_IREAD | _S_IWRITE | _S_IEXEC);
#else
int fd = mkdir(tmpl, S_IRUSR | S_IWUSR | S_IXUSR);
#endif
if (fd >= 0)
{
errno = savedErrno;
return tmpl;
}
else if (errno != EEXIST)
{
return nullptr;
}
}
/* We got out of the loop because we ran out of combinations to try. */
errno = EEXIST;
return nullptr;
}
//#endif
//**************************************************************
// mitk::IOUtil method definitions
namespace mitk
{
struct IOUtil::Impl
{
struct FixedReaderOptionsFunctor : public ReaderOptionsFunctorBase
{
FixedReaderOptionsFunctor(const IFileReader::Options &options) : m_Options(options) {}
bool operator()(LoadInfo &loadInfo) const override
{
IFileReader *reader = loadInfo.m_ReaderSelector.GetSelected().GetReader();
if (reader)
{
reader->SetOptions(m_Options);
}
return false;
}
private:
const IFileReader::Options &m_Options;
};
struct FixedWriterOptionsFunctor : public WriterOptionsFunctorBase
{
FixedWriterOptionsFunctor(const IFileReader::Options &options) : m_Options(options) {}
bool operator()(SaveInfo &saveInfo) const override
{
IFileWriter *writer = saveInfo.m_WriterSelector.GetSelected().GetWriter();
if (writer)
{
writer->SetOptions(m_Options);
}
return false;
}
private:
const IFileWriter::Options &m_Options;
};
static BaseData::Pointer LoadBaseDataFromFile(const std::string &path, const ReaderOptionsFunctorBase* optionsCallback = nullptr);
};
BaseData::Pointer IOUtil::Impl::LoadBaseDataFromFile(const std::string &path,
const ReaderOptionsFunctorBase *optionsCallback)
{
std::vector<BaseData::Pointer> baseDataList = Load(path, optionsCallback);
// The Load(path) call above should throw an exception if nothing could be loaded
assert(!baseDataList.empty());
return baseDataList.front();
}
- std::string IOUtil::Local8BitToUtf8(const std::string& local8BitStr)
- {
-#ifdef US_PLATFORM_WINDOWS
- try
- {
- return WideCharToMultiByte(MultiByteToWideChar(local8BitStr, CP_ACP), CP_UTF8);
- }
- catch (const mitk::Exception&)
- {
- MITK_WARN << "String conversion from current code page to UTF-8 failed. Input string is returned unmodified.";
- }
-#endif
-
- return local8BitStr;
- }
-
- std::string IOUtil::Utf8ToLocal8Bit(const std::string& utf8Str)
- {
-#ifdef US_PLATFORM_WINDOWS
- try
- {
- return WideCharToMultiByte(MultiByteToWideChar(utf8Str, CP_UTF8), CP_ACP);
- }
- catch (const mitk::Exception&)
- {
- MITK_WARN << "String conversion from UTF-8 to current code page failed. Input string is returned unmodified.";
- }
-#endif
-
- return utf8Str;
- }
-
#ifdef US_PLATFORM_WINDOWS
std::string IOUtil::GetProgramPath()
{
char path[512];
std::size_t index = std::string(path, GetModuleFileName(nullptr, path, 512)).find_last_of('\\');
return std::string(path, index);
}
#elif defined(US_PLATFORM_APPLE)
#include <mach-o/dyld.h>
std::string IOUtil::GetProgramPath()
{
char path[512];
uint32_t size = sizeof(path);
if (_NSGetExecutablePath(path, &size) == 0)
{
std::size_t index = std::string(path).find_last_of('/');
std::string strPath = std::string(path, index);
// const char* execPath = strPath.c_str();
// mitk::StandardFileLocations::GetInstance()->AddDirectoryForSearch(execPath,false);
return strPath;
}
return std::string();
}
#else
#include <sstream>
#include <sys/types.h>
#include <unistd.h>
std::string IOUtil::GetProgramPath()
{
std::stringstream ss;
ss << "/proc/" << getpid() << "/exe";
char proc[512] = {0};
ssize_t ch = readlink(ss.str().c_str(), proc, 512);
if (ch == -1)
return std::string();
std::size_t index = std::string(proc).find_last_of('/');
return std::string(proc, index);
}
#endif
char IOUtil::GetDirectorySeparator()
{
#ifdef US_PLATFORM_WINDOWS
return '\\';
#else
return '/';
#endif
}
std::string IOUtil::GetTempPath()
{
static std::string result;
if (result.empty())
{
#ifdef US_PLATFORM_WINDOWS
char tempPathTestBuffer[1];
DWORD bufferLength = ::GetTempPath(1, tempPathTestBuffer);
if (bufferLength == 0)
{
mitkThrow() << GetLastErrorStr();
}
std::vector<char> tempPath(bufferLength);
bufferLength = ::GetTempPath(bufferLength, &tempPath[0]);
if (bufferLength == 0)
{
mitkThrow() << GetLastErrorStr();
}
result.assign(tempPath.begin(), tempPath.begin() + static_cast<std::size_t>(bufferLength));
#else
result = "/tmp/";
#endif
}
return result;
}
std::string IOUtil::CreateTemporaryFile(const std::string &templateName, std::string path)
{
std::ofstream tmpOutputStream;
std::string returnValue = CreateTemporaryFile(tmpOutputStream, templateName, path);
tmpOutputStream.close();
return returnValue;
}
std::string IOUtil::CreateTemporaryFile(std::ofstream &f, const std::string &templateName, std::string path)
{
return CreateTemporaryFile(f, std::ios_base::out | std::ios_base::trunc, templateName, path);
}
std::string IOUtil::CreateTemporaryFile(std::ofstream &f,
std::ios_base::openmode mode,
const std::string &templateName,
std::string path)
{
if (path.empty())
{
path = GetTempPath();
}
path += templateName;
std::vector<char> dst_path(path.begin(), path.end());
dst_path.push_back('\0');
std::size_t lastX = path.find_last_of('X');
std::size_t firstX = path.find_last_not_of('X', lastX);
int firstNonX = firstX == std::string::npos ? -1 : firstX - 1;
while (lastX != std::string::npos && (lastX - firstNonX) < 6)
{
lastX = path.find_last_of('X', firstX);
firstX = path.find_last_not_of('X', lastX);
firstNonX = firstX == std::string::npos ? -1 : firstX - 1;
}
std::size_t suffixlen = lastX == std::string::npos ? path.size() : path.size() - lastX - 1;
int fd = mkstemps_compat(&dst_path[0], suffixlen);
if (fd != -1)
{
path.assign(dst_path.begin(), dst_path.end() - 1);
f.open(path.c_str(), mode | std::ios_base::out | std::ios_base::trunc);
close(fd);
}
else
{
mitkThrow() << "Creating temporary file " << &dst_path[0] << " failed: " << GetLastErrorStr();
}
return path;
}
std::string IOUtil::CreateTemporaryDirectory(const std::string &templateName, std::string path)
{
if (path.empty())
{
path = GetTempPath();
}
path += GetDirectorySeparator() + templateName;
std::vector<char> dst_path(path.begin(), path.end());
dst_path.push_back('\0');
std::size_t lastX = path.find_last_of('X');
std::size_t firstX = path.find_last_not_of('X', lastX);
int firstNonX = firstX == std::string::npos ? -1 : firstX - 1;
while (lastX != std::string::npos && (lastX - firstNonX) < 6)
{
lastX = path.find_last_of('X', firstX);
firstX = path.find_last_not_of('X', lastX);
firstNonX = firstX == std::string::npos ? -1 : firstX - 1;
}
std::size_t suffixlen = lastX == std::string::npos ? path.size() : path.size() - lastX - 1;
if (mkdtemps_compat(&dst_path[0], suffixlen) == nullptr)
{
mitkThrow() << "Creating temporary directory " << &dst_path[0] << " failed: " << GetLastErrorStr();
}
path.assign(dst_path.begin(), dst_path.end() - 1);
return path;
}
DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::string &path, DataStorage &storage, const ReaderOptionsFunctorBase *optionsCallback)
{
std::vector<std::string> paths;
paths.push_back(path);
return Load(paths, storage, optionsCallback);
}
DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::string &path,
const IFileReader::Options &options,
DataStorage &storage)
{
std::vector<LoadInfo> loadInfos;
loadInfos.push_back(LoadInfo(path));
DataStorage::SetOfObjects::Pointer nodeResult = DataStorage::SetOfObjects::New();
Impl::FixedReaderOptionsFunctor optionsCallback(options);
std::string errMsg = Load(loadInfos, nodeResult, &storage, &optionsCallback);
if (!errMsg.empty())
{
mitkThrow() << errMsg;
}
return nodeResult;
}
std::vector<BaseData::Pointer> IOUtil::Load(const std::string &path, const ReaderOptionsFunctorBase *optionsCallback)
{
std::vector<std::string> paths;
paths.push_back(path);
return Load(paths, optionsCallback);
}
std::vector<BaseData::Pointer> IOUtil::Load(const std::string &path, const IFileReader::Options &options)
{
std::vector<LoadInfo> loadInfos;
loadInfos.push_back(LoadInfo(path));
Impl::FixedReaderOptionsFunctor optionsCallback(options);
std::string errMsg = Load(loadInfos, nullptr, nullptr, &optionsCallback);
if (!errMsg.empty())
{
mitkThrow() << errMsg;
}
return loadInfos.front().m_Output;
}
DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::vector<std::string> &paths, DataStorage &storage, const ReaderOptionsFunctorBase *optionsCallback)
{
DataStorage::SetOfObjects::Pointer nodeResult = DataStorage::SetOfObjects::New();
std::vector<LoadInfo> loadInfos;
for (const auto &loadInfo : paths)
{
- loadInfos.push_back(loadInfo);
+ loadInfos.emplace_back(loadInfo);
}
std::string errMsg = Load(loadInfos, nodeResult, &storage, optionsCallback);
if (!errMsg.empty())
{
mitkThrow() << errMsg;
}
return nodeResult;
}
std::vector<BaseData::Pointer> IOUtil::Load(const std::vector<std::string> &paths, const ReaderOptionsFunctorBase *optionsCallback)
{
std::vector<BaseData::Pointer> result;
std::vector<LoadInfo> loadInfos;
for (const auto &loadInfo : paths)
{
- loadInfos.push_back(loadInfo);
+ loadInfos.emplace_back(loadInfo);
}
std::string errMsg = Load(loadInfos, nullptr, nullptr, optionsCallback);
if (!errMsg.empty())
{
mitkThrow() << errMsg;
}
for (std::vector<LoadInfo>::const_iterator iter = loadInfos.begin(), iterEnd = loadInfos.end(); iter != iterEnd;
++iter)
{
result.insert(result.end(), iter->m_Output.begin(), iter->m_Output.end());
}
return result;
}
std::string IOUtil::Load(std::vector<LoadInfo> &loadInfos,
DataStorage::SetOfObjects *nodeResult,
DataStorage *ds,
const ReaderOptionsFunctorBase *optionsCallback)
{
if (loadInfos.empty())
{
return "No input files given";
}
int filesToRead = loadInfos.size();
mitk::ProgressBar::GetInstance()->AddStepsToDo(2 * filesToRead);
std::string errMsg;
std::map<std::string, FileReaderSelector::Item> usedReaderItems;
std::vector< std::string > read_files;
for (auto &loadInfo : loadInfos)
{
if(std::find(read_files.begin(), read_files.end(), loadInfo.m_Path) != read_files.end())
continue;
std::vector<FileReaderSelector::Item> readers = loadInfo.m_ReaderSelector.Get();
if (readers.empty())
{
- if (!itksys::SystemTools::FileExists(loadInfo.m_Path.c_str()))
+ if (!itksys::SystemTools::FileExists(Utf8Util::Local8BitToUtf8(loadInfo.m_Path).c_str()))
{
errMsg += "File '" + loadInfo.m_Path + "' does not exist\n";
}
else
{
errMsg += "No reader available for '" + loadInfo.m_Path + "'\n";
}
continue;
}
bool callOptionsCallback = readers.size() > 1 || !readers.front().GetReader()->GetOptions().empty();
// check if we already used a reader which should be re-used
std::vector<MimeType> currMimeTypes = loadInfo.m_ReaderSelector.GetMimeTypes();
std::string selectedMimeType;
for (std::vector<MimeType>::const_iterator mimeTypeIter = currMimeTypes.begin(),
mimeTypeIterEnd = currMimeTypes.end();
mimeTypeIter != mimeTypeIterEnd;
++mimeTypeIter)
{
std::map<std::string, FileReaderSelector::Item>::const_iterator oldSelectedItemIter =
usedReaderItems.find(mimeTypeIter->GetName());
if (oldSelectedItemIter != usedReaderItems.end())
{
// we found an already used item for a mime-type which is contained
// in the current reader set, check all current readers if there service
// id equals the old reader
for (std::vector<FileReaderSelector::Item>::const_iterator currReaderItem = readers.begin(),
currReaderItemEnd = readers.end();
currReaderItem != currReaderItemEnd;
++currReaderItem)
{
if (currReaderItem->GetMimeType().GetName() == mimeTypeIter->GetName() &&
currReaderItem->GetServiceId() == oldSelectedItemIter->second.GetServiceId() &&
currReaderItem->GetConfidenceLevel() >= oldSelectedItemIter->second.GetConfidenceLevel())
{
// okay, we used the same reader already, re-use its options
selectedMimeType = mimeTypeIter->GetName();
callOptionsCallback = false;
loadInfo.m_ReaderSelector.Select(oldSelectedItemIter->second.GetServiceId());
loadInfo.m_ReaderSelector.GetSelected().GetReader()->SetOptions(
oldSelectedItemIter->second.GetReader()->GetOptions());
break;
}
}
if (!selectedMimeType.empty())
break;
}
}
if (callOptionsCallback && optionsCallback)
{
callOptionsCallback = (*optionsCallback)(loadInfo);
if (!callOptionsCallback && !loadInfo.m_Cancel)
{
usedReaderItems.erase(selectedMimeType);
FileReaderSelector::Item selectedItem = loadInfo.m_ReaderSelector.GetSelected();
usedReaderItems.insert(std::make_pair(selectedItem.GetMimeType().GetName(), selectedItem));
}
}
if (loadInfo.m_Cancel)
{
errMsg += "Reading operation(s) cancelled.";
break;
}
IFileReader *reader = loadInfo.m_ReaderSelector.GetSelected().GetReader();
if (reader == nullptr)
{
errMsg += "Unexpected nullptr reader.";
break;
}
// Do the actual reading
try
{
DataStorage::SetOfObjects::Pointer nodes;
if (ds != nullptr)
{
nodes = reader->Read(*ds);
std::vector< std::string > new_files = reader->GetReadFiles();
read_files.insert( read_files.end(), new_files.begin(), new_files.end() );
}
else
{
nodes = DataStorage::SetOfObjects::New();
std::vector<mitk::BaseData::Pointer> baseData = reader->Read();
for (auto iter = baseData.begin(); iter != baseData.end(); ++iter)
{
if (iter->IsNotNull())
{
mitk::DataNode::Pointer node = mitk::DataNode::New();
node->SetData(*iter);
nodes->InsertElement(nodes->Size(), node);
}
}
std::vector< std::string > new_files = reader->GetReadFiles();
read_files.insert( read_files.end(), new_files.begin(), new_files.end() );
}
for (DataStorage::SetOfObjects::ConstIterator nodeIter = nodes->Begin(), nodeIterEnd = nodes->End();
nodeIter != nodeIterEnd;
++nodeIter)
{
const mitk::DataNode::Pointer &node = nodeIter->Value();
mitk::BaseData::Pointer data = node->GetData();
if (data.IsNull())
{
continue;
}
- auto path = Local8BitToUtf8(loadInfo.m_Path);
- auto pathProp = mitk::StringProperty::New(path);
- data->SetProperty("path", pathProp);
+ data->SetProperty("path", mitk::StringProperty::New(Utf8Util::Local8BitToUtf8(loadInfo.m_Path)));
loadInfo.m_Output.push_back(data);
if (nodeResult)
{
nodeResult->push_back(nodeIter->Value());
}
}
if (loadInfo.m_Output.empty() || (nodeResult && nodeResult->Size() == 0))
{
errMsg += "Unknown read error occurred reading " + loadInfo.m_Path;
}
}
catch (const std::exception &e)
{
errMsg += "Exception occured when reading file " + loadInfo.m_Path + ":\n" + e.what() + "\n\n";
}
mitk::ProgressBar::GetInstance()->Progress(2);
--filesToRead;
}
if (!errMsg.empty())
{
MITK_ERROR << errMsg;
}
mitk::ProgressBar::GetInstance()->Progress(2 * filesToRead);
return errMsg;
}
std::vector<BaseData::Pointer> IOUtil::Load(const us::ModuleResource &usResource, std::ios_base::openmode mode)
{
us::ModuleResourceStream resStream(usResource, mode);
mitk::CoreServicePointer<mitk::IMimeTypeProvider> mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider());
std::vector<MimeType> mimetypes = mimeTypeProvider->GetMimeTypesForFile(usResource.GetResourcePath());
std::vector<mitk::BaseData::Pointer> data;
if (mimetypes.empty())
{
mitkThrow() << "No mimetype for resource stream: " << usResource.GetResourcePath();
return data;
}
mitk::FileReaderRegistry fileReaderRegistry;
std::vector<us::ServiceReference<IFileReader>> refs = fileReaderRegistry.GetReferences(mimetypes[0]);
if (refs.empty())
{
mitkThrow() << "No reader available for resource stream: " << usResource.GetResourcePath();
return data;
}
mitk::IFileReader *reader = fileReaderRegistry.GetReader(refs[0]);
reader->SetInput(usResource.GetResourcePath(), &resStream);
data = reader->Read();
return data;
}
void IOUtil::Save(const BaseData *data, const std::string &path, bool setPathProperty) { Save(data, path, IFileWriter::Options(), setPathProperty); }
void IOUtil::Save(const BaseData *data, const std::string &path, const IFileWriter::Options &options, bool setPathProperty)
{
Save(data, std::string(), path, options, setPathProperty);
}
void IOUtil::Save(const BaseData *data, const std::string &mimeType, const std::string &path, bool addExtension, bool setPathProperty)
{
Save(data, mimeType, path, IFileWriter::Options(), addExtension, setPathProperty);
}
void IOUtil::Save(const BaseData *data,
const std::string &mimeType,
const std::string &path,
const IFileWriter::Options &options,
bool addExtension,
bool setPathProperty)
{
if ((data == nullptr) || (data->IsEmpty()))
mitkThrow() << "BaseData cannotbe null or empty for save methods in IOUtil.h.";
std::string errMsg;
if (options.empty())
{
errMsg = Save(data, mimeType, path, nullptr, addExtension, setPathProperty);
}
else
{
Impl::FixedWriterOptionsFunctor optionsCallback(options);
errMsg = Save(data, mimeType, path, &optionsCallback, addExtension, setPathProperty);
}
if (!errMsg.empty())
{
mitkThrow() << errMsg;
}
}
void IOUtil::Save(std::vector<IOUtil::SaveInfo> &saveInfos, bool setPathProperty)
{
std::string errMsg = Save(saveInfos, nullptr, setPathProperty);
if (!errMsg.empty())
{
mitkThrow() << errMsg;
}
}
std::string IOUtil::Save(const BaseData *data,
const std::string &mimeTypeName,
const std::string &path,
WriterOptionsFunctorBase *optionsCallback,
bool addExtension,
bool setPathProperty)
{
if (path.empty())
{
return "No output filename given";
}
mitk::CoreServicePointer<mitk::IMimeTypeProvider> mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider());
MimeType mimeType = mimeTypeProvider->GetMimeTypeForName(mimeTypeName);
SaveInfo saveInfo(data, mimeType, path);
- std::string ext = itksys::SystemTools::GetFilenameExtension(path);
+ std::string ext = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetFilenameExtension(Utf8Util::Local8BitToUtf8(path)));
if (saveInfo.m_WriterSelector.IsEmpty())
{
return std::string("No suitable writer found for the current data of type ") + data->GetNameOfClass() +
(mimeType.IsValid() ? (std::string(" and mime-type ") + mimeType.GetName()) : std::string()) +
(ext.empty() ? std::string() : (std::string(" with extension ") + ext));
}
// Add an extension if not already specified
if (ext.empty() && addExtension)
{
ext = saveInfo.m_MimeType.GetExtensions().empty() ? std::string() : "." + saveInfo.m_MimeType.GetExtensions().front();
saveInfo.m_Path += ext;
}
std::vector<SaveInfo> infos;
infos.push_back(saveInfo);
return Save(infos, optionsCallback, setPathProperty);
}
std::string IOUtil::Save(std::vector<SaveInfo> &saveInfos, WriterOptionsFunctorBase *optionsCallback, bool setPathProperty)
{
if (saveInfos.empty())
{
return "No data for saving available";
}
int filesToWrite = saveInfos.size();
mitk::ProgressBar::GetInstance()->AddStepsToDo(2 * filesToWrite);
std::string errMsg;
std::set<SaveInfo> usedSaveInfos;
for (auto &saveInfo : saveInfos)
{
const std::string baseDataType = saveInfo.m_BaseData->GetNameOfClass();
std::vector<FileWriterSelector::Item> writers = saveInfo.m_WriterSelector.Get();
// Error out if no compatible Writer was found
if (writers.empty())
{
errMsg += std::string("No writer available for ") + baseDataType + " data.\n";
continue;
}
bool callOptionsCallback = writers.size() > 1 || !writers[0].GetWriter()->GetOptions().empty();
// check if we already used a writer for this base data type
// which should be re-used
auto oldSaveInfoIter = usedSaveInfos.find(saveInfo);
if (oldSaveInfoIter != usedSaveInfos.end())
{
// we previously saved a base data object of the same data with the same mime-type,
// check if the same writer is contained in the current writer set and if the
// confidence level matches
FileWriterSelector::Item oldSelectedItem =
oldSaveInfoIter->m_WriterSelector.Get(oldSaveInfoIter->m_WriterSelector.GetSelectedId());
for (std::vector<FileWriterSelector::Item>::const_iterator currWriterItem = writers.begin(),
currWriterItemEnd = writers.end();
currWriterItem != currWriterItemEnd;
++currWriterItem)
{
if (currWriterItem->GetServiceId() == oldSelectedItem.GetServiceId() &&
currWriterItem->GetConfidenceLevel() >= oldSelectedItem.GetConfidenceLevel())
{
// okay, we used the same writer already, re-use its options
callOptionsCallback = false;
saveInfo.m_WriterSelector.Select(oldSaveInfoIter->m_WriterSelector.GetSelectedId());
saveInfo.m_WriterSelector.GetSelected().GetWriter()->SetOptions(oldSelectedItem.GetWriter()->GetOptions());
break;
}
}
}
if (callOptionsCallback && optionsCallback)
{
callOptionsCallback = (*optionsCallback)(saveInfo);
if (!callOptionsCallback && !saveInfo.m_Cancel)
{
usedSaveInfos.erase(saveInfo);
usedSaveInfos.insert(saveInfo);
}
}
if (saveInfo.m_Cancel)
{
errMsg += "Writing operation(s) cancelled.";
break;
}
IFileWriter *writer = saveInfo.m_WriterSelector.GetSelected().GetWriter();
if (writer == nullptr)
{
errMsg += "Unexpected nullptr writer.";
break;
}
// Do the actual writing
try
{
writer->SetOutputLocation(saveInfo.m_Path);
writer->Write();
}
catch (const std::exception &e)
{
errMsg += std::string("Exception occurred when writing to ") + saveInfo.m_Path + ":\n" + e.what() + "\n";
}
if (setPathProperty)
- saveInfo.m_BaseData->GetPropertyList()->SetStringProperty("path", Local8BitToUtf8(saveInfo.m_Path).c_str());
+ saveInfo.m_BaseData->GetPropertyList()->SetStringProperty("path", Utf8Util::Local8BitToUtf8(saveInfo.m_Path).c_str());
mitk::ProgressBar::GetInstance()->Progress(2);
--filesToWrite;
}
if (!errMsg.empty())
{
MITK_ERROR << errMsg;
}
mitk::ProgressBar::GetInstance()->Progress(2 * filesToWrite);
return errMsg;
}
IOUtil::SaveInfo::SaveInfo(const BaseData *baseData, const MimeType &mimeType, const std::string &path)
: m_BaseData(baseData),
m_WriterSelector(baseData, mimeType.GetName(), path),
m_MimeType(mimeType.IsValid() ? mimeType // use the original mime-type
:
(m_WriterSelector.IsEmpty() ?
mimeType // no writer found, use the original invalid mime-type
:
m_WriterSelector.GetDefault().GetMimeType() // use the found default mime-type
)),
m_Path(path),
m_Cancel(false)
{
}
bool IOUtil::SaveInfo::operator<(const IOUtil::SaveInfo &other) const
{
int r = strcmp(m_BaseData->GetNameOfClass(), other.m_BaseData->GetNameOfClass());
if (r == 0)
{
return m_WriterSelector.GetSelected().GetMimeType() < other.m_WriterSelector.GetSelected().GetMimeType();
}
return r < 0;
}
IOUtil::LoadInfo::LoadInfo(const std::string &path) : m_Path(path), m_ReaderSelector(path), m_Cancel(false) {}
}
diff --git a/Modules/Core/src/IO/mitkItkImageIO.cpp b/Modules/Core/src/IO/mitkItkImageIO.cpp
index dcf50ecff8..e4672cba4a 100644
--- a/Modules/Core/src/IO/mitkItkImageIO.cpp
+++ b/Modules/Core/src/IO/mitkItkImageIO.cpp
@@ -1,754 +1,754 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkItkImageIO.h"
#include <mitkArbitraryTimeGeometry.h>
#include <mitkCoreServices.h>
#include <mitkCustomMimeType.h>
#include <mitkIOMimeTypes.h>
#include <mitkIPropertyPersistence.h>
#include <mitkImage.h>
#include <mitkImageReadAccessor.h>
#include <mitkLocaleSwitch.h>
#include <mitkUIDManipulator.h>
#include <itkImage.h>
#include <itkImageFileReader.h>
#include <itkImageIOFactory.h>
#include <itkImageIORegion.h>
#include <itkMetaDataObject.h>
#include <algorithm>
namespace mitk
{
const char *const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type";
const char *const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints";
const char *const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type";
const char *const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints";
const char* const PROPERTY_KEY_UID = "org_mitk_uid";
ItkImageIO::ItkImageIO(const ItkImageIO &other)
: AbstractFileIO(other), m_ImageIO(dynamic_cast<itk::ImageIOBase *>(other.m_ImageIO->Clone().GetPointer()))
{
this->InitializeDefaultMetaDataKeys();
}
std::vector<std::string> ItkImageIO::FixUpImageIOExtensions(const std::string &imageIOName)
{
std::vector<std::string> extensions;
// Try to fix-up some known ITK image IO classes
if (imageIOName == "GiplImageIO")
{
extensions.push_back("gipl");
extensions.push_back("gipl.gz");
}
else if (imageIOName == "GDCMImageIO")
{
extensions.push_back("gdcm");
extensions.push_back("dcm");
extensions.push_back("DCM");
extensions.push_back("dc3");
extensions.push_back("DC3");
extensions.push_back("ima");
extensions.push_back("img");
}
else if (imageIOName == "PNGImageIO")
{
extensions.push_back("png");
extensions.push_back("PNG");
}
else if (imageIOName == "StimulateImageIO")
{
extensions.push_back("spr");
}
else if (imageIOName == "HDF5ImageIO")
{
extensions.push_back("hdf");
extensions.push_back("h4");
extensions.push_back("hdf4");
extensions.push_back("h5");
extensions.push_back("hdf5");
extensions.push_back("he4");
extensions.push_back("he5");
extensions.push_back("hd5");
}
else if ("GE4ImageIO" == imageIOName || "GE5ImageIO" == imageIOName || "Bruker2dseqImageIO" == imageIOName)
{
extensions.push_back("");
}
if (!extensions.empty())
{
MITK_DEBUG << "Fixing up known extensions for " << imageIOName;
}
return extensions;
}
void ItkImageIO::FixUpCustomMimeTypeName(const std::string &imageIOName, CustomMimeType &customMimeType)
{
if ("GE4ImageIO" == imageIOName)
{
customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "ge4");
}
else if ("GE5ImageIO" == imageIOName)
{
customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "ge5");
}
else if ("Bruker2dseqImageIO" == imageIOName)
{
customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "bruker2dseq");
}
}
ItkImageIO::ItkImageIO(itk::ImageIOBase::Pointer imageIO)
: AbstractFileIO(Image::GetStaticNameOfClass()), m_ImageIO(imageIO)
{
if (m_ImageIO.IsNull())
{
mitkThrow() << "ITK ImageIOBase argument must not be nullptr";
}
this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image.");
this->InitializeDefaultMetaDataKeys();
std::vector<std::string> readExtensions = m_ImageIO->GetSupportedReadExtensions();
if (readExtensions.empty())
{
std::string imageIOName = m_ImageIO->GetNameOfClass();
MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide read extensions";
readExtensions = FixUpImageIOExtensions(imageIOName);
}
CustomMimeType customReaderMimeType;
customReaderMimeType.SetCategory("Images");
for (std::vector<std::string>::const_iterator iter = readExtensions.begin(), endIter = readExtensions.end();
iter != endIter;
++iter)
{
std::string extension = *iter;
if (!extension.empty() && extension[0] == '.')
{
extension.assign(iter->begin() + 1, iter->end());
}
customReaderMimeType.AddExtension(extension);
}
auto extensions = customReaderMimeType.GetExtensions();
if (extensions.empty() || (extensions.size() == 1 && extensions[0].empty()))
{
std::string imageIOName = m_ImageIO->GetNameOfClass();
FixUpCustomMimeTypeName(imageIOName, customReaderMimeType);
}
this->AbstractFileReader::SetMimeType(customReaderMimeType);
std::vector<std::string> writeExtensions = imageIO->GetSupportedWriteExtensions();
if (writeExtensions.empty())
{
std::string imageIOName = imageIO->GetNameOfClass();
MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide write extensions";
writeExtensions = FixUpImageIOExtensions(imageIOName);
}
if (writeExtensions != readExtensions)
{
CustomMimeType customWriterMimeType;
customWriterMimeType.SetCategory("Images");
for (std::vector<std::string>::const_iterator iter = writeExtensions.begin(), endIter = writeExtensions.end();
iter != endIter;
++iter)
{
std::string extension = *iter;
if (!extension.empty() && extension[0] == '.')
{
extension.assign(iter->begin() + 1, iter->end());
}
customWriterMimeType.AddExtension(extension);
}
auto extensions = customWriterMimeType.GetExtensions();
if (extensions.empty() || (extensions.size() == 1 && extensions[0].empty()))
{
std::string imageIOName = m_ImageIO->GetNameOfClass();
FixUpCustomMimeTypeName(imageIOName, customWriterMimeType);
}
this->AbstractFileWriter::SetMimeType(customWriterMimeType);
}
std::string description = std::string("ITK ") + imageIO->GetNameOfClass();
this->SetReaderDescription(description);
this->SetWriterDescription(description);
this->RegisterService();
}
ItkImageIO::ItkImageIO(const CustomMimeType &mimeType, itk::ImageIOBase::Pointer imageIO, int rank)
: AbstractFileIO(Image::GetStaticNameOfClass(), mimeType, std::string("ITK ") + imageIO->GetNameOfClass()),
m_ImageIO(imageIO)
{
if (m_ImageIO.IsNull())
{
mitkThrow() << "ITK ImageIOBase argument must not be nullptr";
}
this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image.");
this->InitializeDefaultMetaDataKeys();
if (rank)
{
this->AbstractFileReader::SetRanking(rank);
this->AbstractFileWriter::SetRanking(rank);
}
this->RegisterService();
}
std::vector<TimePointType> ConvertMetaDataObjectToTimePointList(const itk::MetaDataObjectBase* data)
{
const auto* timeGeometryTimeData =
dynamic_cast<const itk::MetaDataObject<std::string>*>(data);
std::vector<TimePointType> result;
if (timeGeometryTimeData)
{
std::string dataStr = timeGeometryTimeData->GetMetaDataObjectValue();
std::stringstream stream(dataStr);
TimePointType tp;
while (stream >> tp)
{
result.push_back(tp);
}
}
return result;
};
itk::MetaDataObjectBase::Pointer ConvertTimePointListToMetaDataObject(const mitk::TimeGeometry* timeGeometry)
{
std::stringstream stream;
stream << timeGeometry->GetTimeBounds(0)[0];
const auto maxTimePoints = timeGeometry->CountTimeSteps();
for (TimeStepType pos = 0; pos < maxTimePoints; ++pos)
{
auto timeBounds = timeGeometry->GetTimeBounds(pos);
///////////////////////////////////////
// Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details.
// This workarround should be removed as soon as T28262 is solved!
if (pos + 1 == maxTimePoints && timeBounds[0]==timeBounds[1])
{
timeBounds[1] = timeBounds[0] + 1.;
}
// End of workarround for T27883
//////////////////////////////////////
stream << " " << timeBounds[1];
}
auto result = itk::MetaDataObject<std::string>::New();
result->SetMetaDataObjectValue(stream.str());
return result.GetPointer();
};
std::vector<BaseData::Pointer> ItkImageIO::DoRead()
{
std::vector<BaseData::Pointer> result;
mitk::LocaleSwitch localeSwitch("C");
Image::Pointer image = Image::New();
const unsigned int MINDIM = 2;
const unsigned int MAXDIM = 4;
const std::string path = this->GetLocalFileName();
MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl;
// Check to see if we can read the file given the name or prefix
if (path.empty())
{
mitkThrow() << "Empty filename in mitk::ItkImageIO ";
}
// Got to allocate space for the image. Determine the characteristics of
// the image.
m_ImageIO->SetFileName(path);
m_ImageIO->ReadImageInformation();
unsigned int ndim = m_ImageIO->GetNumberOfDimensions();
if (ndim < MINDIM || ndim > MAXDIM)
{
MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim
<< " dimensions! Reading as 4D.";
ndim = MAXDIM;
}
itk::ImageIORegion ioRegion(ndim);
itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize();
itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex();
unsigned int dimensions[MAXDIM];
dimensions[0] = 0;
dimensions[1] = 0;
dimensions[2] = 0;
dimensions[3] = 0;
ScalarType spacing[MAXDIM];
spacing[0] = 1.0f;
spacing[1] = 1.0f;
spacing[2] = 1.0f;
spacing[3] = 1.0f;
Point3D origin;
origin.Fill(0);
unsigned int i;
for (i = 0; i < ndim; ++i)
{
ioStart[i] = 0;
ioSize[i] = m_ImageIO->GetDimensions(i);
if (i < MAXDIM)
{
dimensions[i] = m_ImageIO->GetDimensions(i);
spacing[i] = m_ImageIO->GetSpacing(i);
if (spacing[i] <= 0)
spacing[i] = 1.0f;
}
if (i < 3)
{
origin[i] = m_ImageIO->GetOrigin(i);
}
}
ioRegion.SetSize(ioSize);
ioRegion.SetIndex(ioStart);
MITK_INFO << "ioRegion: " << ioRegion << std::endl;
m_ImageIO->SetIORegion(ioRegion);
void *buffer = new unsigned char[m_ImageIO->GetImageSizeInBytes()];
m_ImageIO->Read(buffer);
image->Initialize(MakePixelType(m_ImageIO), ndim, dimensions);
image->SetImportChannel(buffer, 0, Image::ManageMemory);
const itk::MetaDataDictionary &dictionary = m_ImageIO->GetMetaDataDictionary();
// access direction of itk::Image and include spacing
mitk::Matrix3D matrix;
matrix.SetIdentity();
unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim);
for (i = 0; i < itkDimMax3; ++i)
for (j = 0; j < itkDimMax3; ++j)
matrix[i][j] = m_ImageIO->GetDirection(j)[i];
// re-initialize PlaneGeometry with origin and direction
PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0);
planeGeometry->SetOrigin(origin);
planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix);
// re-initialize SlicedGeometry3D
SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0);
slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2));
slicedGeometry->SetSpacing(spacing);
MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false);
MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true);
// re-initialize TimeGeometry
TimeGeometry::Pointer timeGeometry;
if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE))
{ // also check for the name because of backwards compatibility. Past code version stored with the name and not with
// the key
itk::MetaDataObject<std::string>::ConstPointer timeGeometryTypeData = nullptr;
if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE))
{
timeGeometryTypeData =
dynamic_cast<const itk::MetaDataObject<std::string> *>(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TYPE));
}
else
{
timeGeometryTypeData =
dynamic_cast<const itk::MetaDataObject<std::string> *>(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TYPE));
}
if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass())
{
MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass();
typedef std::vector<TimePointType> TimePointVector;
TimePointVector timePoints;
if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS))
{
timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS));
}
else if (dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS))
{
timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS));
}
if (timePoints.empty())
{
MITK_ERROR << "Stored timepoints are empty. Meta information seems to bee invalid. Switch to ProportionalTimeGeometry fallback";
}
else if (timePoints.size() - 1 != image->GetDimension(3))
{
MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension ("
<< image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback";
}
else
{
ArbitraryTimeGeometry::Pointer arbitraryTimeGeometry = ArbitraryTimeGeometry::New();
TimePointVector::const_iterator pos = timePoints.begin();
auto prePos = pos++;
for (; pos != timePoints.end(); ++prePos, ++pos)
{
arbitraryTimeGeometry->AppendNewTimeStepClone(slicedGeometry, *prePos, *pos);
}
timeGeometry = arbitraryTimeGeometry;
}
}
}
if (timeGeometry.IsNull())
{ // Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry
MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass();
ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New();
propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3));
timeGeometry = propTimeGeometry;
}
image->SetTimeGeometry(timeGeometry);
buffer = nullptr;
MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents();
for (auto iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd;
++iter)
{
if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string))
{
const std::string &key = iter->first;
std::string assumedPropertyName = key;
std::replace(assumedPropertyName.begin(), assumedPropertyName.end(), '_', '.');
std::string mimeTypeName = GetMimeType()->GetName();
// Check if there is already a info for the key and our mime type.
mitk::CoreServicePointer<IPropertyPersistence> propPersistenceService(mitk::CoreServices::GetPropertyPersistence());
IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfoByKey(key);
auto predicate = [&mimeTypeName](const PropertyPersistenceInfo::ConstPointer &x) {
return x.IsNotNull() && x->GetMimeTypeName() == mimeTypeName;
};
auto finding = std::find_if(infoList.begin(), infoList.end(), predicate);
if (finding == infoList.end())
{
auto predicateWild = [](const PropertyPersistenceInfo::ConstPointer &x) {
return x.IsNotNull() && x->GetMimeTypeName() == PropertyPersistenceInfo::ANY_MIMETYPE_NAME();
};
finding = std::find_if(infoList.begin(), infoList.end(), predicateWild);
}
PropertyPersistenceInfo::ConstPointer info;
if (finding != infoList.end())
{
assumedPropertyName = (*finding)->GetName();
info = *finding;
}
else
{ // we have not found anything suitable so we generate our own info
auto newInfo = PropertyPersistenceInfo::New();
newInfo->SetNameAndKey(assumedPropertyName, key);
newInfo->SetMimeTypeName(PropertyPersistenceInfo::ANY_MIMETYPE_NAME());
info = newInfo;
}
std::string value =
dynamic_cast<itk::MetaDataObject<std::string> *>(iter->second.GetPointer())->GetMetaDataObjectValue();
mitk::BaseProperty::Pointer loadedProp = info->GetDeserializationFunction()(value);
if (loadedProp.IsNull())
{
MITK_ERROR << "Property cannot be correctly deserialized and is skipped. Check if data format is valid. Problematic property value string: \"" << value << "\"; Property info used to deserialized: " << info;
break;
}
image->SetProperty(assumedPropertyName.c_str(), loadedProp);
// Read properties should be persisted unless they are default properties
// which are written anyway
bool isDefaultKey(false);
for (const auto &defaultKey : m_DefaultMetaDataKeys)
{
if (defaultKey.length() <= assumedPropertyName.length())
{
// does the start match the default key
if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos)
{
isDefaultKey = true;
break;
}
}
}
if (!isDefaultKey)
{
propPersistenceService->AddInfo(info);
}
}
}
// Handle UID
if (dictionary.HasKey(PROPERTY_KEY_UID))
{
itk::MetaDataObject<std::string>::ConstPointer uidData = dynamic_cast<const itk::MetaDataObject<std::string>*>(dictionary.Get(PROPERTY_KEY_UID));
if (uidData.IsNotNull())
{
mitk::UIDManipulator uidManipulator(image);
uidManipulator.SetUID(uidData->GetMetaDataObjectValue());
}
}
MITK_INFO << "...finished!";
result.push_back(image.GetPointer());
return result;
}
AbstractFileIO::ConfidenceLevel ItkImageIO::GetReaderConfidenceLevel() const
{
return m_ImageIO->CanReadFile(GetLocalFileName().c_str()) ? IFileReader::Supported : IFileReader::Unsupported;
}
void ItkImageIO::Write()
{
const auto *image = dynamic_cast<const mitk::Image *>(this->GetInput());
if (image == nullptr)
{
mitkThrow() << "Cannot write non-image data";
}
// Switch the current locale to "C"
LocaleSwitch localeSwitch("C");
// Clone the image geometry, because we might have to change it
// for writing purposes
BaseGeometry::Pointer geometry = image->GetGeometry()->Clone();
// Check if geometry information will be lost
if (image->GetDimension() == 2 && !geometry->Is2DConvertable())
{
MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might "
"consider using Convert2Dto3DImageFilter before saving.";
// set matrix to identity
mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New();
affTrans->SetIdentity();
mitk::Vector3D spacing = geometry->GetSpacing();
mitk::Point3D origin = geometry->GetOrigin();
geometry->SetIndexToWorldTransform(affTrans);
geometry->SetSpacing(spacing);
geometry->SetOrigin(origin);
}
LocalFile localFile(this);
const std::string path = localFile.GetFileName();
MITK_INFO << "Writing image: " << path << std::endl;
try
{
// Implementation of writer using itkImageIO directly. This skips the use
// of templated itkImageFileWriter, which saves the multiplexing on MITK side.
const unsigned int dimension = image->GetDimension();
const unsigned int *const dimensions = image->GetDimensions();
const mitk::PixelType pixelType = image->GetPixelType();
const mitk::Vector3D mitkSpacing = geometry->GetSpacing();
const mitk::Point3D mitkOrigin = geometry->GetOrigin();
// Due to templating in itk, we are forced to save a 4D spacing and 4D Origin,
// though they are not supported in MITK
itk::Vector<double, 4u> spacing4D;
spacing4D[0] = mitkSpacing[0];
spacing4D[1] = mitkSpacing[1];
spacing4D[2] = mitkSpacing[2];
spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here
itk::Vector<double, 4u> origin4D;
origin4D[0] = mitkOrigin[0];
origin4D[1] = mitkOrigin[1];
origin4D[2] = mitkOrigin[2];
origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here
// Set the necessary information for imageIO
m_ImageIO->SetNumberOfDimensions(dimension);
m_ImageIO->SetPixelType(pixelType.GetPixelType());
- m_ImageIO->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ?
- static_cast<itk::ImageIOBase::IOComponentType>(pixelType.GetComponentType()) :
- itk::ImageIOBase::UNKNOWNCOMPONENTTYPE);
+ m_ImageIO->SetComponentType(static_cast<int>(pixelType.GetComponentType()) < PixelComponentUserType
+ ? pixelType.GetComponentType()
+ : itk::IOComponentEnum::UNKNOWNCOMPONENTTYPE);
m_ImageIO->SetNumberOfComponents(pixelType.GetNumberOfComponents());
itk::ImageIORegion ioRegion(dimension);
for (unsigned int i = 0; i < dimension; i++)
{
m_ImageIO->SetDimensions(i, dimensions[i]);
m_ImageIO->SetSpacing(i, spacing4D[i]);
m_ImageIO->SetOrigin(i, origin4D[i]);
- mitk::Vector3D mitkDirection;
- mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i));
+ mitk::Vector3D mitkDirection(0.0);
+ mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i).as_ref());
itk::Vector<double, 4u> direction4D;
direction4D[0] = mitkDirection[0];
direction4D[1] = mitkDirection[1];
direction4D[2] = mitkDirection[2];
// MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must
// save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix.
if (i == 3)
{
direction4D[3] = 1; // homogenous component
}
else
{
direction4D[3] = 0;
}
vnl_vector<double> axisDirection(dimension);
for (unsigned int j = 0; j < dimension; j++)
{
axisDirection[j] = direction4D[j] / spacing4D[i];
}
m_ImageIO->SetDirection(i, axisDirection);
ioRegion.SetSize(i, image->GetLargestPossibleRegion().GetSize(i));
ioRegion.SetIndex(i, image->GetLargestPossibleRegion().GetIndex(i));
}
// use compression if available
m_ImageIO->UseCompressionOn();
m_ImageIO->SetIORegion(ioRegion);
m_ImageIO->SetFileName(path);
// Handle time geometry
const auto *arbitraryTG = dynamic_cast<const ArbitraryTimeGeometry *>(image->GetTimeGeometry());
if (arbitraryTG)
{
itk::EncapsulateMetaData<std::string>(m_ImageIO->GetMetaDataDictionary(),
PROPERTY_KEY_TIMEGEOMETRY_TYPE,
ArbitraryTimeGeometry::GetStaticNameOfClass());
auto metaTimePoints = ConvertTimePointListToMetaDataObject(arbitraryTG);
m_ImageIO->GetMetaDataDictionary().Set(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, metaTimePoints);
}
// Handle properties
mitk::PropertyList::Pointer imagePropertyList = image->GetPropertyList();
for (const auto &property : *imagePropertyList->GetMap())
{
mitk::CoreServicePointer<IPropertyPersistence> propPersistenceService(mitk::CoreServices::GetPropertyPersistence());
IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfo(property.first, GetMimeType()->GetName(), true);
if (infoList.empty())
{
continue;
}
std::string value = mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING;
try
{
value = infoList.front()->GetSerializationFunction()(property.second);
}
catch (const std::exception& e)
{
MITK_ERROR << "Error when serializing content of property. This often indicates the use of an out dated reader. Property will not be stored. Skipped property: " << property.first << ". Reason: " << e.what();
}
catch (...)
{
MITK_ERROR << "Unkown error when serializing content of property. This often indicates the use of an out dated reader. Property will not be stored. Skipped property: " << property.first;
}
if (value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING)
{
continue;
}
std::string key = infoList.front()->GetKey();
itk::EncapsulateMetaData<std::string>(m_ImageIO->GetMetaDataDictionary(), key, value);
}
// Handle UID
itk::EncapsulateMetaData<std::string>(m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_UID, image->GetUID());
ImageReadAccessor imageAccess(image);
LocaleSwitch localeSwitch2("C");
m_ImageIO->Write(imageAccess.GetData());
}
catch (const std::exception &e)
{
mitkThrow() << e.what();
}
}
AbstractFileIO::ConfidenceLevel ItkImageIO::GetWriterConfidenceLevel() const
{
// Check if the image dimension is supported
const auto *image = dynamic_cast<const Image *>(this->GetInput());
if (image == nullptr)
{
// We cannot write a null object, DUH!
return IFileWriter::Unsupported;
}
if (!m_ImageIO->SupportsDimension(image->GetDimension()))
{
// okay, dimension is not supported. We have to look at a special case:
// 3D-Image with one slice. We can treat that as a 2D image.
if ((image->GetDimension() == 3) && (image->GetSlicedGeometry()->GetSlices() == 1))
return IFileWriter::Supported;
else
return IFileWriter::Unsupported;
}
// Check if geometry information will be lost
if (image->GetDimension() == 2 && !image->GetGeometry()->Is2DConvertable())
{
return IFileWriter::PartiallySupported;
}
return IFileWriter::Supported;
}
ItkImageIO *ItkImageIO::IOClone() const { return new ItkImageIO(*this); }
void ItkImageIO::InitializeDefaultMetaDataKeys()
{
this->m_DefaultMetaDataKeys.push_back("NRRD.space");
this->m_DefaultMetaDataKeys.push_back("NRRD.kinds");
this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE);
this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS);
this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName");
}
}
diff --git a/Modules/Core/src/IO/mitkLog.cpp b/Modules/Core/src/IO/mitkLog.cpp
index de655bfa80..64fbe3a00a 100644
--- a/Modules/Core/src/IO/mitkLog.cpp
+++ b/Modules/Core/src/IO/mitkLog.cpp
@@ -1,249 +1,249 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <mitkExceptionMacro.h>
#include <mitkLog.h>
#include <mitkLogMacros.h>
#include <itkOutputWindow.h>
-#include <itkSimpleFastMutexLock.h>
#include <cstdio>
#include <fstream>
#include <iostream>
+#include <mutex>
-static itk::SimpleFastMutexLock logMutex;
+static std::mutex logMutex;
static mitk::LoggingBackend *mitkLogBackend = nullptr;
static std::ofstream *logFile = nullptr;
static std::string logFileName = "";
static std::stringstream *outputWindow = nullptr;
static bool logOutputWindow = false;
void mitk::LoggingBackend::EnableAdditionalConsoleWindow(bool enable)
{
logOutputWindow = enable;
}
void mitk::LoggingBackend::ProcessMessage(const mbilog::LogMessage &l)
{
- logMutex.Lock();
+ logMutex.lock();
#ifdef _WIN32
FormatSmart(l, (int)GetCurrentThreadId());
#else
FormatSmart(l);
#endif
if (logFile)
{
#ifdef _WIN32
FormatFull(*logFile, l, (int)GetCurrentThreadId());
#else
FormatFull(*logFile, l);
#endif
}
if (logOutputWindow)
{
if (outputWindow == nullptr)
{
outputWindow = new std::stringstream();
}
outputWindow->str("");
outputWindow->clear();
#ifdef _WIN32
FormatFull(*outputWindow, l, (int)GetCurrentThreadId());
#else
FormatFull(*outputWindow, l);
#endif
itk::OutputWindow::GetInstance()->DisplayText(outputWindow->str().c_str());
}
- logMutex.Unlock();
+ logMutex.unlock();
}
void mitk::LoggingBackend::Register()
{
if (mitkLogBackend)
return;
mitkLogBackend = new mitk::LoggingBackend();
mbilog::RegisterBackend(mitkLogBackend);
}
void mitk::LoggingBackend::Unregister()
{
if (mitkLogBackend)
{
SetLogFile(nullptr);
mbilog::UnregisterBackend(mitkLogBackend);
delete mitkLogBackend;
mitkLogBackend = nullptr;
}
}
void mitk::LoggingBackend::SetLogFile(const char *file)
{
// closing old logfile
{
bool closed = false;
std::string closedFileName;
- logMutex.Lock();
+ logMutex.lock();
if (logFile)
{
closed = true;
closedFileName = logFileName;
logFile->close();
delete logFile;
logFile = nullptr;
logFileName = "";
}
- logMutex.Unlock();
+ logMutex.unlock();
if (closed)
{
MITK_INFO << "closing logfile (" << closedFileName << ")";
}
}
// opening new logfile
if (file)
{
- logMutex.Lock();
+ logMutex.lock();
logFileName = file;
logFile = new std::ofstream();
logFile->open(file, std::ios_base::out | std::ios_base::app);
if (logFile->good())
{
- logMutex.Unlock();
+ logMutex.unlock();
MITK_INFO << "Logfile: " << logFileName;
}
else
{
delete logFile;
logFile = nullptr;
- logMutex.Unlock();
+ logMutex.unlock();
MITK_WARN << "opening logfile '" << file << "' for writing failed";
}
// mutex is now unlocked
}
}
std::string mitk::LoggingBackend::GetLogFile()
{
return logFileName;
}
void mitk::LoggingBackend::CatchLogFileCommandLineParameter(int &argc, char **argv)
{
int r;
for (r = 1; r < argc; r++)
{
if (std::string(argv[r]) == "--logfile")
{
if (r + 1 >= argc)
{
--argc;
MITK_ERROR << "--logfile parameter found, but no file given";
return;
}
mitk::LoggingBackend::SetLogFile(argv[r + 1]);
for (r += 2; r < argc; r++)
argv[r - 2] = argv[r];
argc -= 2;
return;
}
}
}
void mitk::LoggingBackend::RotateLogFiles(const std::string &prefixPath)
{
static const int numLogFiles = 10;
std::string newEmptyLogFileName;
// first: rotate the old log files to get a new, free logfile name
newEmptyLogFileName = IncrementLogFileNames(prefixPath, numLogFiles);
// now: use the new empty logfile name as name for this run
mitk::LoggingBackend::SetLogFile(newEmptyLogFileName.c_str());
}
std::string mitk::LoggingBackend::IncrementLogFileNames(const std::string &prefixPath, int numLogFiles)
{
// delete last one
{
std::stringstream s;
s << prefixPath.c_str() << "-" << numLogFiles - 1 << ".log";
// check if the file exists
if (CheckIfFileExists(s.str())) // if yes: delete it
{
int retVal = ::remove(s.str().c_str());
if (retVal != 0)
{
mitkThrow()
<< "Problem while deleting the oldest log file. Maybe the access to this files is blocked. Aborting!";
}
}
}
// rename the others
for (int r = numLogFiles - 1; r >= 1; r--)
{
std::stringstream dst;
dst << prefixPath.c_str() << "-" << r << ".log";
std::stringstream src;
src << prefixPath.c_str() << "-" << r - 1 << ".log";
// check if the source exists
if (CheckIfFileExists(src.str())) // if yes: rename it
{
int retVal = ::rename(src.str().c_str(), dst.str().c_str());
if (retVal != 0)
{
mitkThrow() << "Problem while renaming the log files. Maybe the access to this files is blocked. Aborting!";
}
}
}
// create new empty name and return it
{
std::stringstream s;
s << prefixPath.c_str() << "-0.log";
return s.str();
}
}
bool mitk::LoggingBackend::CheckIfFileExists(const std::string &filename)
{
bool returnValue = false;
std::ifstream File(filename.c_str());
if (File.good())
{
returnValue = true;
}
else
{
returnValue = false;
}
File.close();
return returnValue;
}
mbilog::OutputType mitk::LoggingBackend::GetOutputType() const
{
return mbilog::Console;
}
diff --git a/Modules/Core/src/IO/mitkPixelType.cpp b/Modules/Core/src/IO/mitkPixelType.cpp
index 93c47fee9e..0d4c304d91 100644
--- a/Modules/Core/src/IO/mitkPixelType.cpp
+++ b/Modules/Core/src/IO/mitkPixelType.cpp
@@ -1,178 +1,178 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkPixelType.h"
#include <mitkLogMacros.h>
mitk::PixelType::PixelType(const mitk::PixelType &other)
: m_ComponentType(other.m_ComponentType),
m_PixelType(other.m_PixelType),
m_ComponentTypeName(other.m_ComponentTypeName),
m_PixelTypeName(other.m_PixelTypeName),
m_NumberOfComponents(other.m_NumberOfComponents),
m_BytesPerComponent(other.m_BytesPerComponent)
{
}
mitk::PixelType &mitk::PixelType::operator=(const PixelType &other)
{
m_ComponentType = other.m_ComponentType;
m_PixelType = other.m_PixelType;
m_ComponentTypeName = other.m_ComponentTypeName;
m_PixelTypeName = other.m_PixelTypeName;
m_NumberOfComponents = other.m_NumberOfComponents;
m_BytesPerComponent = other.m_BytesPerComponent;
return *this;
}
-itk::ImageIOBase::IOPixelType mitk::PixelType::GetPixelType() const
+mitk::PixelType::ItkIOPixelType mitk::PixelType::GetPixelType() const
{
return m_PixelType;
}
-int mitk::PixelType::GetComponentType() const
+mitk::PixelType::ItkIOComponentType mitk::PixelType::GetComponentType() const
{
return m_ComponentType;
}
std::string mitk::PixelType::GetPixelTypeAsString() const
{
return m_PixelTypeName;
}
std::string mitk::PixelType::GetComponentTypeAsString() const
{
return m_ComponentTypeName;
}
std::string mitk::PixelType::GetTypeAsString() const
{
return m_PixelTypeName + " (" + m_ComponentTypeName + ")";
}
size_t mitk::PixelType::GetSize() const
{
return (m_NumberOfComponents * m_BytesPerComponent);
}
size_t mitk::PixelType::GetBpe() const
{
return this->GetSize() * 8;
}
size_t mitk::PixelType::GetNumberOfComponents() const
{
return m_NumberOfComponents;
}
size_t mitk::PixelType::GetBitsPerComponent() const
{
return m_BytesPerComponent * 8;
}
mitk::PixelType::~PixelType()
{
}
-mitk::PixelType::PixelType(const int componentType,
- const ItkIOPixelType pixelType,
+mitk::PixelType::PixelType(ItkIOComponentType componentType,
+ ItkIOPixelType pixelType,
std::size_t bytesPerComponent,
std::size_t numberOfComponents,
const std::string &componentTypeName,
const std::string &pixelTypeName)
: m_ComponentType(componentType),
m_PixelType(pixelType),
m_ComponentTypeName(componentTypeName),
m_PixelTypeName(pixelTypeName),
m_NumberOfComponents(numberOfComponents),
m_BytesPerComponent(bytesPerComponent)
{
}
bool mitk::PixelType::operator==(const mitk::PixelType &rhs) const
{
bool returnValue = ( this->m_PixelType == rhs.m_PixelType
&& this->m_ComponentType == rhs.m_ComponentType
&& this->m_NumberOfComponents == rhs.m_NumberOfComponents
&& this->m_BytesPerComponent == rhs.m_BytesPerComponent );
MITK_DEBUG << "|> mitk::PixelType::operator== rhs, lhs: \n"
<< "| m_BytesPerComponent = " << m_BytesPerComponent << ", " << rhs.m_BytesPerComponent << '\n'
<< "| m_NumberOfComponents = " << m_NumberOfComponents << ", " << rhs.m_NumberOfComponents << '\n'
<< "| m_PixelTypeName = " << m_PixelTypeName << ", " << rhs.m_PixelTypeName << '\n'
<< "| m_ComponentTypeName = " << m_ComponentTypeName << ", " << rhs.m_ComponentTypeName << '\n'
<< "| m_PixelType = " << m_PixelType << ", " << rhs.m_PixelType << '\n'
<< "| m_ComponentType = " << m_ComponentType << ", " << rhs.m_ComponentType
<< ", returnValue = " << returnValue << (returnValue ? "[True]" : "[False]") << ". <|";
return returnValue;
}
bool mitk::PixelType::operator!=(const mitk::PixelType &rhs) const
{
return !(this->operator==(rhs));
}
mitk::PixelType mitk::MakePixelType(vtkImageData *vtkimagedata)
{
int numOfComponents = vtkimagedata->GetNumberOfScalarComponents();
switch (vtkimagedata->GetScalarType())
{
case VTK_BIT:
case VTK_CHAR:
return mitk::MakePixelType<char, char>(numOfComponents);
break;
case VTK_UNSIGNED_CHAR:
return mitk::MakePixelType<unsigned char, unsigned char>(numOfComponents);
break;
case VTK_SHORT:
return mitk::MakePixelType<short, short>(numOfComponents);
break;
case VTK_UNSIGNED_SHORT:
return mitk::MakePixelType<unsigned short, unsigned short>(numOfComponents);
break;
case VTK_INT:
return mitk::MakePixelType<int, int>(numOfComponents);
break;
case VTK_UNSIGNED_INT:
return mitk::MakePixelType<unsigned int, unsigned int>(numOfComponents);
break;
case VTK_LONG:
return mitk::MakePixelType<long, long>(numOfComponents);
break;
case VTK_UNSIGNED_LONG:
return mitk::MakePixelType<unsigned long, unsigned long>(numOfComponents);
break;
case VTK_FLOAT:
return mitk::MakePixelType<float, float>(numOfComponents);
break;
case VTK_DOUBLE:
return mitk::MakePixelType<double, double>(numOfComponents);
break;
default:
break;
}
mitkThrow() << "tried to make pixeltype from vtkimage of unknown data type(short, char, int, ...)";
}
diff --git a/Modules/Core/src/IO/mitkRawImageFileReader.h b/Modules/Core/src/IO/mitkRawImageFileReader.h
index 3ad97d9199..8e80383d4d 100644
--- a/Modules/Core/src/IO/mitkRawImageFileReader.h
+++ b/Modules/Core/src/IO/mitkRawImageFileReader.h
@@ -1,54 +1,51 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKRAWIMAGEFILEREADER_H_
#define MITKRAWIMAGEFILEREADER_H_
#include "mitkAbstractFileReader.h"
namespace mitk
{
/**
* The user must set the dimensionality, the dimensions and the pixel type.
* If they are incorrect, the image will not be opened or the visualization will be incorrect.
*/
class RawImageFileReaderService : public AbstractFileReader
{
public:
/** Supported pixel types. */
typedef enum { UCHAR, SCHAR, USHORT, SSHORT, UINT, SINT, FLOAT, DOUBLE } IOPixelType;
/** Endianity of bits. */
typedef enum { LITTLE, BIG } EndianityType;
RawImageFileReaderService();
protected:
RawImageFileReaderService(const RawImageFileReaderService &other);
std::vector<itk::SmartPointer<BaseData>> DoRead() override;
using mitk::AbstractFileReader::Read;
private:
template <typename TPixel, unsigned int VImageDimensions>
mitk::BaseData::Pointer TypedRead(const std::string &path, EndianityType endianity, int *size);
RawImageFileReaderService *Clone() const override;
-
- /** Vector containing dimensions of image to be read. */
- itk::Vector<int, 3> m_Dimensions;
};
} // namespace mitk
#endif /* MITKRAWIMAGEFILEREADER_H_ */
diff --git a/Modules/Core/src/IO/mitkStandardFileLocations.cpp b/Modules/Core/src/IO/mitkStandardFileLocations.cpp
index d92045120e..07b069fece 100644
--- a/Modules/Core/src/IO/mitkStandardFileLocations.cpp
+++ b/Modules/Core/src/IO/mitkStandardFileLocations.cpp
@@ -1,238 +1,239 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <mitkStandardFileLocations.h>
#include <itkMacro.h>
#include <itkObject.h>
#include <itksys/SystemTools.hxx>
#include <mitkConfig.h>
+#include <mitkUtf8Util.h>
#include <algorithm>
mitk::StandardFileLocations::StandardFileLocations()
{
}
mitk::StandardFileLocations::~StandardFileLocations()
{
}
mitk::StandardFileLocations *mitk::StandardFileLocations::GetInstance()
{
static StandardFileLocations::Pointer m_Instance = nullptr;
if (m_Instance.IsNull())
m_Instance = StandardFileLocations::New();
return m_Instance;
}
void mitk::StandardFileLocations::AddDirectoryForSearch(const char *dir, bool insertInFrontOfSearchList)
{
// Do nothing if directory is already included into search list (TODO more clever: search only once!)
FileSearchVectorType::iterator iter;
if (m_SearchDirectories.size() > 0)
{
iter = std::find(m_SearchDirectories.begin(), m_SearchDirectories.end(), std::string(dir));
if (iter != m_SearchDirectories.end())
return;
}
// insert dir into queue
if (insertInFrontOfSearchList)
{
auto it = m_SearchDirectories.begin();
m_SearchDirectories.insert(it, std::string(dir));
}
else
m_SearchDirectories.push_back(std::string(dir));
}
void mitk::StandardFileLocations::RemoveDirectoryForSearch(const char *dir)
{
FileSearchVectorType::iterator it;
// background layers
if (m_SearchDirectories.size() > 0)
{
it = std::find(m_SearchDirectories.begin(), m_SearchDirectories.end(), std::string(dir));
if (it != m_SearchDirectories.end())
{
m_SearchDirectories.erase(it);
return;
}
}
}
std::string mitk::StandardFileLocations::SearchDirectoriesForFile(const char *filename)
{
FileSearchVectorType::iterator it;
for (it = m_SearchDirectories.begin(); it != m_SearchDirectories.end(); ++it)
{
std::string currDir = (*it);
// Perhaps append "/" before appending filename
if (currDir.find_last_of("\\") + 1 != currDir.size() || currDir.find_last_of("/") + 1 != currDir.size())
currDir += "/";
// Append filename
currDir += filename;
// Perhaps remove "/" after filename
if (currDir.find_last_of("\\") + 1 == currDir.size() || currDir.find_last_of("/") + 1 == currDir.size())
currDir.erase(currDir.size() - 1, currDir.size());
// convert to OS dependent path schema
- currDir = itksys::SystemTools::ConvertToOutputPath(currDir.c_str());
+ currDir = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::ConvertToOutputPath(Utf8Util::Local8BitToUtf8(currDir).c_str()));
// On windows systems, the ConvertToOutputPath method quotes pathes that contain empty spaces.
// These quotes are not expected by the FileExists method and therefore removed, if existing.
if (currDir.find_last_of("\"") + 1 == currDir.size())
currDir.erase(currDir.size() - 1, currDir.size());
if (currDir.find_last_of("\"") == 0)
currDir.erase(0, 1);
// Return first found path
- if (itksys::SystemTools::FileExists(currDir.c_str()))
+ if (itksys::SystemTools::FileExists(Utf8Util::Local8BitToUtf8(currDir).c_str()))
return currDir;
}
return std::string("");
}
std::string mitk::StandardFileLocations::FindFile(const char *filename, const char *pathInSourceDir)
{
std::string directoryPath;
// 1. look for MITKCONF environment variable
const char *mitkConf = itksys::SystemTools::GetEnv("MITKCONF");
if (mitkConf != nullptr)
AddDirectoryForSearch(mitkConf, false);
// 2. use .mitk-subdirectory in home directory of the user
#if defined(_WIN32) && !defined(__CYGWIN__)
const char *homeDrive = itksys::SystemTools::GetEnv("HOMEDRIVE");
const char *homePath = itksys::SystemTools::GetEnv("HOMEPATH");
if ((homeDrive != nullptr) || (homePath != nullptr))
{
directoryPath = homeDrive;
directoryPath += homePath;
directoryPath += "/.mitk/";
AddDirectoryForSearch(directoryPath.c_str(), false);
}
#else
const char *homeDirectory = itksys::SystemTools::GetEnv("HOME");
if (homeDirectory != nullptr)
{
directoryPath = homeDirectory;
directoryPath += "/.mitk/";
AddDirectoryForSearch(directoryPath.c_str(), false);
}
#endif // defined(_WIN32) && !defined(__CYGWIN__)
// 3. look in the current working directory
directoryPath = "";
AddDirectoryForSearch(directoryPath.c_str());
- directoryPath = itksys::SystemTools::GetCurrentWorkingDirectory();
+ directoryPath = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetCurrentWorkingDirectory());
AddDirectoryForSearch(directoryPath.c_str(), false);
std::string directoryBinPath = directoryPath + "/bin";
AddDirectoryForSearch(directoryBinPath.c_str(), false);
// 4. use a source tree location from compile time
directoryPath = MITK_ROOT;
if (pathInSourceDir)
{
directoryPath += pathInSourceDir;
}
directoryPath += '/';
AddDirectoryForSearch(directoryPath.c_str(), false);
return SearchDirectoriesForFile(filename);
}
std::string mitk::StandardFileLocations::GetOptionDirectory()
{
const char *mitkoptions = itksys::SystemTools::GetEnv("MITKOPTIONS");
std::string optionsDirectory;
if (mitkoptions != nullptr)
{
// 1. look for MITKOPTIONS environment variable
optionsDirectory = mitkoptions;
optionsDirectory += "/";
}
else
{
// 2. use .mitk-subdirectory in home directory of the user
std::string homeDirectory;
#if defined(_WIN32) && !defined(__CYGWIN__)
const char *homeDrive = itksys::SystemTools::GetEnv("HOMEDRIVE");
const char *homePath = itksys::SystemTools::GetEnv("HOMEPATH");
if ((homeDrive == nullptr) || (homePath == nullptr))
{
itkGenericOutputMacro(<< "Environment variables HOMEDRIVE and/or HOMEPATH not set"
<< ". Using current working directory as home directory: "
- << itksys::SystemTools::GetCurrentWorkingDirectory());
- homeDirectory = itksys::SystemTools::GetCurrentWorkingDirectory();
+ << Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetCurrentWorkingDirectory()));
+ homeDirectory = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetCurrentWorkingDirectory());
}
else
{
homeDirectory = homeDrive;
homeDirectory += homePath;
}
- if (itksys::SystemTools::FileExists(homeDirectory.c_str()) == false)
+ if (itksys::SystemTools::FileExists(Utf8Util::Local8BitToUtf8(homeDirectory).c_str()) == false)
{
itkGenericOutputMacro(<< "Could not find home directory at " << homeDirectory
<< ". Using current working directory as home directory: "
- << itksys::SystemTools::GetCurrentWorkingDirectory());
- homeDirectory = itksys::SystemTools::GetCurrentWorkingDirectory();
+ << Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetCurrentWorkingDirectory()));
+ homeDirectory = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetCurrentWorkingDirectory());
}
#else
const char *home = itksys::SystemTools::GetEnv("HOME");
if (home == nullptr)
{
itkGenericOutputMacro(<< "Environment variable HOME not set"
<< ". Using current working directory as home directory: "
<< itksys::SystemTools::GetCurrentWorkingDirectory());
homeDirectory = itksys::SystemTools::GetCurrentWorkingDirectory();
}
else
homeDirectory = home;
if (itksys::SystemTools::FileExists(homeDirectory.c_str()) == false)
{
itkGenericOutputMacro(<< "Could not find home directory at " << homeDirectory
<< ". Using current working directory as home directory: "
<< itksys::SystemTools::GetCurrentWorkingDirectory());
homeDirectory = itksys::SystemTools::GetCurrentWorkingDirectory();
}
#endif // defined(_WIN32) && !defined(__CYGWIN__)
optionsDirectory = homeDirectory;
optionsDirectory += "/.mitk";
}
- optionsDirectory = itksys::SystemTools::ConvertToOutputPath(optionsDirectory.c_str());
+ optionsDirectory = itksys::SystemTools::ConvertToOutputPath(Utf8Util::Local8BitToUtf8(optionsDirectory).c_str());
if (itksys::SystemTools::CountChar(optionsDirectory.c_str(), '"') > 0)
{
char *unquoted = itksys::SystemTools::RemoveChars(optionsDirectory.c_str(), "\"");
optionsDirectory = unquoted;
delete[] unquoted;
}
if (itksys::SystemTools::MakeDirectory(optionsDirectory.c_str()) == false)
{
- itkGenericExceptionMacro(<< "Could not create .mitk directory at " << optionsDirectory);
+ itkGenericExceptionMacro(<< "Could not create .mitk directory at " << Utf8Util::Utf8ToLocal8Bit(optionsDirectory));
}
- return optionsDirectory;
+ return Utf8Util::Utf8ToLocal8Bit(optionsDirectory);
}
diff --git a/Modules/Core/src/IO/mitkSurfaceVtkIO.cpp b/Modules/Core/src/IO/mitkSurfaceVtkIO.cpp
index 8ffbd3bb34..a8a1af5ec1 100644
--- a/Modules/Core/src/IO/mitkSurfaceVtkIO.cpp
+++ b/Modules/Core/src/IO/mitkSurfaceVtkIO.cpp
@@ -1,97 +1,98 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkSurfaceVtkIO.h"
#include "mitkSurface.h"
+#include <mitkUtf8Util.h>
#include <vtkLinearTransform.h>
#include <vtkPolyData.h>
#include <vtkSmartPointer.h>
#include <vtkTransformPolyDataFilter.h>
#include <itksys/SystemTools.hxx>
namespace mitk
{
SurfaceVtkIO::SurfaceVtkIO(const std::string &baseDataType,
const CustomMimeType &mimeType,
const std::string &description)
: AbstractFileIO(baseDataType, mimeType, description)
{
}
vtkSmartPointer<vtkPolyData> SurfaceVtkIO::GetPolyData(unsigned int t, std::string &fileName)
{
const auto *input = dynamic_cast<const Surface *>(this->GetInput());
vtkSmartPointer<vtkTransformPolyDataFilter> transformPolyData = vtkSmartPointer<vtkTransformPolyDataFilter>::New();
// surfaces do not have to exist in all timesteps; therefor, only write valid surfaces
if (input->GetVtkPolyData(t) == nullptr)
return vtkSmartPointer<vtkPolyData>();
std::string baseName = this->GetOutputLocation();
- std::string extension = itksys::SystemTools::GetFilenameExtension(baseName);
+ std::string extension = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetFilenameExtension(Utf8Util::Local8BitToUtf8(baseName)));
if (!extension.empty())
{
baseName = baseName.substr(0, baseName.size() - extension.size());
}
std::ostringstream ss;
ss.imbue(::std::locale::classic());
BaseGeometry *geometry = input->GetGeometry(t);
if (input->GetTimeGeometry()->IsValidTimeStep(t))
{
if (input->GetTimeGeometry()->CountTimeSteps() > 1)
{
const TimeBounds &timebounds = input->GetTimeGeometry()->GetTimeBounds(t);
ss << baseName << "_S" << std::setprecision(0) << timebounds[0] << "_E" << std::setprecision(0) << timebounds[1]
<< "_T" << t << extension;
}
else
{
// use the original file name
ss << this->GetOutputLocation();
}
}
else
{
MITK_WARN << "Error on write: TimeGeometry invalid of surface " << fileName << ".";
return vtkSmartPointer<vtkPolyData>();
}
fileName = ss.str();
transformPolyData->SetInputData(input->GetVtkPolyData(t));
transformPolyData->SetTransform(geometry->GetVtkTransform());
transformPolyData->UpdateWholeExtent();
vtkSmartPointer<vtkPolyData> polyData = transformPolyData->GetOutput();
return polyData;
}
IFileIO::ConfidenceLevel SurfaceVtkIO::GetWriterConfidenceLevel() const
{
if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported)
return Unsupported;
if (this->GetInput()->GetTimeGeometry()->CountTimeSteps() > 1)
{
// The VTK formats don't support multiple time points.
// During writing, we write each time step into a separate file.
// For output streams, we only write the first time-step and print a warning.
return PartiallySupported;
}
return Supported;
}
}
diff --git a/Modules/Core/src/IO/mitkUtf8Util.cpp b/Modules/Core/src/IO/mitkUtf8Util.cpp
new file mode 100644
index 0000000000..ee1604f144
--- /dev/null
+++ b/Modules/Core/src/IO/mitkUtf8Util.cpp
@@ -0,0 +1,87 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#include <mitkUtf8Util.h>
+#include <mitkExceptionMacro.h>
+
+#include <usGlobalConfig.h>
+
+#ifdef US_PLATFORM_WINDOWS
+
+#include <Windows.h>
+
+namespace
+{
+ std::wstring MultiByteToWideChar(const std::string& mbString, UINT codePage)
+ {
+ auto numChars = ::MultiByteToWideChar(codePage, 0, mbString.data(), mbString.size(), nullptr, 0);
+
+ if (0 >= numChars)
+ mitkThrow() << "Failure to convert multi-byte character string to wide character string";
+
+ std::wstring wString;
+ wString.resize(numChars);
+
+ ::MultiByteToWideChar(codePage, 0, mbString.data(), mbString.size(), &wString[0], static_cast<int>(wString.size()));
+
+ return wString;
+ }
+
+ std::string WideCharToMultiByte(const std::wstring& wString, UINT codePage)
+ {
+ auto numChars = ::WideCharToMultiByte(codePage, 0, wString.data(), wString.size(), nullptr, 0, nullptr, nullptr);
+
+ if (0 >= numChars)
+ mitkThrow() << "Failure to convert wide character string to multi-byte character string";
+
+ std::string mbString;
+ mbString.resize(numChars);
+
+ ::WideCharToMultiByte(codePage, 0, wString.data(), wString.size(), &mbString[0], static_cast<int>(mbString.size()), nullptr, nullptr);
+
+ return mbString;
+ }
+}
+
+#endif
+
+std::string mitk::Utf8Util::Local8BitToUtf8(const std::string& local8BitStr)
+{
+#ifdef US_PLATFORM_WINDOWS
+ try
+ {
+ return WideCharToMultiByte(MultiByteToWideChar(local8BitStr, CP_ACP), CP_UTF8);
+ }
+ catch (const mitk::Exception&)
+ {
+ MITK_WARN << "String conversion from current code page to UTF-8 failed. Input string is returned unmodified.";
+ }
+#endif
+
+ return local8BitStr;
+}
+
+std::string mitk::Utf8Util::Utf8ToLocal8Bit(const std::string& utf8Str)
+{
+#ifdef US_PLATFORM_WINDOWS
+ try
+ {
+ return WideCharToMultiByte(MultiByteToWideChar(utf8Str, CP_UTF8), CP_ACP);
+ }
+ catch (const mitk::Exception&)
+ {
+ MITK_WARN << "String conversion from UTF-8 to current code page failed. Input string is returned unmodified.";
+ }
+#endif
+
+ return utf8Str;
+}
diff --git a/Modules/Core/src/Interactions/mitkDataInteractor.cpp b/Modules/Core/src/Interactions/mitkDataInteractor.cpp
index 26fc0e78c1..e80023d1db 100644
--- a/Modules/Core/src/Interactions/mitkDataInteractor.cpp
+++ b/Modules/Core/src/Interactions/mitkDataInteractor.cpp
@@ -1,98 +1,105 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkDataInteractor.h"
#include "mitkDataNode.h"
#include "mitkStateMachineState.h"
+namespace mitk
+{
+ itkEventMacroDefinition(DataInteractorEvent, itk::AnyEvent);
+ itkEventMacroDefinition(StartInteraction, DataInteractorEvent);
+ itkEventMacroDefinition(ResultReady, DataInteractorEvent);
+}
+
// Predefined internal events/signals
const std::string mitk::DataInteractor::IntDeactivateMe = "DeactivateMe";
const std::string mitk::DataInteractor::IntLeaveWidget = "LeaveWidget";
const std::string mitk::DataInteractor::IntEnterWidget = "EnterWidget";
mitk::DataInteractor::DataInteractor()
{
}
mitk::DataInteractor::~DataInteractor()
{
if (!m_DataNode.IsExpired())
{
auto dataNode = m_DataNode.Lock();
if (dataNode->GetDataInteractor() == this)
dataNode->SetDataInteractor(nullptr);
}
}
mitk::DataNode *mitk::DataInteractor::GetDataNode() const
{
return m_DataNode.Lock();
}
void mitk::DataInteractor::SetDataNode(DataNode *dataNode)
{
if (dataNode == m_DataNode)
return;
if (!m_DataNode.IsExpired())
m_DataNode.Lock()->SetDataInteractor(nullptr);
m_DataNode = dataNode;
if (dataNode != nullptr)
m_DataNode.Lock()->SetDataInteractor(this);
this->DataNodeChanged();
}
int mitk::DataInteractor::GetLayer() const
{
int layer = -1;
if (!m_DataNode.IsExpired())
m_DataNode.Lock()->GetIntProperty("layer", layer);
return layer;
}
void mitk::DataInteractor::ConnectActionsAndFunctions()
{
MITK_WARN << "DataInteractor::ConnectActionsAndFunctions() is not implemented.";
}
mitk::ProcessEventMode mitk::DataInteractor::GetMode() const
{
auto mode = this->GetCurrentState()->GetMode();
if (mode == "PREFER_INPUT")
return PREFERINPUT;
if (mode == "GRAB_INPUT")
return GRABINPUT;
return REGULAR;
}
void mitk::DataInteractor::NotifyStart()
{
this->GetDataNode()->InvokeEvent(StartInteraction());
}
void mitk::DataInteractor::NotifyResultReady()
{
this->GetDataNode()->InvokeEvent(ResultReady());
}
void mitk::DataInteractor::DataNodeChanged()
{
}
diff --git a/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp b/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp
index fb52e35151..5740ea3a58 100644
--- a/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp
+++ b/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp
@@ -1,914 +1,914 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkDisplayActionEventBroadcast.h"
// us
#include "usGetModuleContext.h"
#include "usModuleContext.h"
// mitk core module
#include <mitkCompositePixelValueToString.h>
#include <mitkDisplayActionEvents.h>
#include <mitkImage.h>
#include <mitkImagePixelReadAccessor.h>
#include <mitkInteractionConst.h>
#include <mitkInteractionPositionEvent.h>
#include <mitkLine.h>
#include <mitkNodePredicateDataType.h>
#include <mitkPixelTypeMultiplex.h>
#include <mitkRotationOperation.h>
#include <mitkStatusBar.h>
#include <rotate_cursor.xpm>
mitk::DisplayActionEventBroadcast::DisplayActionEventBroadcast()
: m_AlwaysReact(false)
, m_AutoRepeat(false)
, m_IndexToSliceModifier(4)
, m_InvertScrollDirection(false)
, m_InvertZoomDirection(false)
, m_ZoomFactor(2)
, m_InvertMoveDirection(false)
, m_InvertLevelWindowDirection(false)
, m_LinkPlanes(true)
{
m_StartCoordinateInMM.Fill(0);
m_LastDisplayCoordinate.Fill(0);
m_LastCoordinateInMM.Fill(0);
m_CurrentDisplayCoordinate.Fill(0);
// register the broadcast class (itself) as an interaction event observer via micro services
us::ServiceProperties props;
props["name"] = std::string("DisplayActionEventBroadcast");
m_ServiceRegistration = us::GetModuleContext()->RegisterService<InteractionEventObserver>(this, props);
}
mitk::DisplayActionEventBroadcast::~DisplayActionEventBroadcast()
{
m_ServiceRegistration.Unregister();
}
void mitk::DisplayActionEventBroadcast::Notify(InteractionEvent* interactionEvent, bool isHandled)
{
// the event is passed to the state machine interface to be handled
if (!isHandled || m_AlwaysReact)
{
HandleEvent(interactionEvent, nullptr);
}
}
void mitk::DisplayActionEventBroadcast::ConnectActionsAndFunctions()
{
CONNECT_CONDITION("check_position_event", CheckPositionEvent);
CONNECT_CONDITION("check_can_rotate", CheckRotationPossible);
CONNECT_CONDITION("check_can_swivel", CheckSwivelPossible);
CONNECT_FUNCTION("init", Init);
CONNECT_FUNCTION("move", Move);
CONNECT_FUNCTION("zoom", Zoom);
CONNECT_FUNCTION("scroll", Scroll);
CONNECT_FUNCTION("ScrollOneUp", ScrollOneUp);
CONNECT_FUNCTION("ScrollOneDown", ScrollOneDown);
CONNECT_FUNCTION("levelWindow", AdjustLevelWindow);
CONNECT_FUNCTION("setCrosshair", SetCrosshair);
CONNECT_FUNCTION("updateStatusbar", UpdateStatusbar)
CONNECT_FUNCTION("startRotation", StartRotation);
CONNECT_FUNCTION("endRotation", EndRotation);
CONNECT_FUNCTION("rotate", Rotate);
CONNECT_FUNCTION("swivel", Swivel);
CONNECT_FUNCTION("IncreaseTimeStep", IncreaseTimeStep);
CONNECT_FUNCTION("DecreaseTimeStep", DecreaseTimeStep);
}
void mitk::DisplayActionEventBroadcast::ConfigurationChanged()
{
PropertyList::Pointer properties = GetAttributes();
// allwaysReact
std::string strAlwaysReact = "";
m_AlwaysReact = false;
if (properties->GetStringProperty("alwaysReact", strAlwaysReact))
{
if (strAlwaysReact == "true")
{
m_AlwaysReact = true;
}
}
// auto repeat
std::string strAutoRepeat = "";
m_AutoRepeat = false;
if (properties->GetStringProperty("autoRepeat", strAutoRepeat))
{
if (strAutoRepeat == "true")
{
m_AutoRepeat = true;
}
}
// pixel movement for scrolling one slice
std::string strPixelPerSlice = "";
m_IndexToSliceModifier = 4;
if (properties->GetStringProperty("pixelPerSlice", strPixelPerSlice))
{
m_IndexToSliceModifier = atoi(strPixelPerSlice.c_str());
}
// scroll direction
if (!properties->GetStringProperty("scrollDirection", m_ScrollDirection))
{
m_ScrollDirection = "updown";
}
m_InvertScrollDirection = GetBoolProperty(properties, "invertScrollDirection", false);
// zoom direction
if (!properties->GetStringProperty("zoomDirection", m_ZoomDirection))
{
m_ZoomDirection = "updown";
}
m_InvertZoomDirection = GetBoolProperty(properties, "invertZoomDirection", false);
m_InvertMoveDirection = GetBoolProperty(properties, "invertMoveDirection", false);
if (!properties->GetStringProperty("levelWindowDirection", m_LevelDirection))
{
m_LevelDirection = "leftright";
}
m_InvertLevelWindowDirection = GetBoolProperty(properties, "invertLevelWindowDirection", false);
// coupled rotation
std::string strCoupled = "";
m_LinkPlanes = false;
if (properties->GetStringProperty("coupled", strCoupled))
{
if (strCoupled == "true")
{
m_LinkPlanes = true;
}
}
// zoom factor
std::string strZoomFactor = "";
properties->GetStringProperty("zoomFactor", strZoomFactor);
m_ZoomFactor = .05;
if (atoi(strZoomFactor.c_str()) > 0)
{
m_ZoomFactor = 1.0 + (atoi(strZoomFactor.c_str()) / 100.0);
}
}
bool mitk::DisplayActionEventBroadcast::FilterEvents(InteractionEvent* interactionEvent, DataNode * /*dataNode*/)
{
BaseRenderer* sendingRenderer = interactionEvent->GetSender();
if (nullptr == sendingRenderer)
{
return false;
}
if (BaseRenderer::Standard3D == sendingRenderer->GetMapperID())
{
return false;
}
return true;
}
bool mitk::DisplayActionEventBroadcast::CheckPositionEvent(const InteractionEvent *interactionEvent)
{
const auto* positionEvent = dynamic_cast<const InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return false;
}
return true;
}
bool mitk::DisplayActionEventBroadcast::CheckRotationPossible(const InteractionEvent *interactionEvent)
{
// Decide between moving and rotation slices.
/*
Detailed logic:
1. Find the SliceNavigationController that has sent the event: this one defines our rendering plane and will NOT be
rotated. Needs not even be counted or checked.
2. Inspect every other SliceNavigationController
- calculate the line intersection of this SliceNavigationController's plane with our rendering plane
- if there is NO intersection, ignore and continue
- IF there is an intersection
- check the mouse cursor's distance from that line.
0. if the line is NOT near the cursor, remember the plane as "one of the other planes" (which can be rotated in
"locked" mode)
1. on first line near the cursor, just remember this intersection line as THE other plane that we want to rotate
2. on every consecutive line near the cursor, check if the line is geometrically identical to the line that we want to
rotate
- if yes, we just push this line to the "other" lines and rotate it along
- if no, then we have a situation where the mouse is near two other lines (e.g. crossing point) and don't want to
rotate
*/
const auto* positionEvent = dynamic_cast<const InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return false;
}
BaseRenderer* renderer = positionEvent->GetSender();
if (nullptr == renderer)
{
return false;
}
const PlaneGeometry* rendererWorldPlaneGeometry = renderer->GetCurrentWorldPlaneGeometry();
if (nullptr == rendererWorldPlaneGeometry)
{
return false;
}
Point3D position = positionEvent->GetPositionInWorld();
const auto spacing = rendererWorldPlaneGeometry->GetSpacing();
const PlaneGeometry *geometryToBeRotated = nullptr; // this one is under the mouse cursor
const PlaneGeometry *anyOtherGeometry = nullptr; // this is also visible (for calculation of intersection ONLY)
Line3D intersectionLineWithGeometryToBeRotated;
bool hitMultipleLines(false);
m_SNCsToBeRotated.clear();
const ScalarType threshholdDistancePixels = 12.0;
auto allRenderWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows();
for (auto renderWindow : allRenderWindows)
{
SliceNavigationController* snc = BaseRenderer::GetInstance(renderWindow)->GetSliceNavigationController();
// If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes.
if (BaseRenderer::Standard3D == BaseRenderer::GetInstance(renderWindow)->GetMapperID())
{
continue;
}
const PlaneGeometry* rendererPlaneGeometry = snc->GetCurrentPlaneGeometry();
if (nullptr == rendererPlaneGeometry)
{
continue; // ignore, we don't see a plane
}
// check if there is an intersection between rendered / clicked geometry and the one being analyzed
Line3D intersectionLine;
if (!rendererWorldPlaneGeometry->IntersectionLine(rendererPlaneGeometry, intersectionLine))
{
continue; // we ignore this plane, it's parallel to our plane
}
// check distance from intersection line
const double distanceFromIntersectionLine = intersectionLine.Distance(position) / spacing[snc->GetDefaultViewDirection()];
// far away line, only remember for linked rotation if necessary
if (distanceFromIntersectionLine > threshholdDistancePixels)
{
// we just take the last one, so overwrite each iteration (we just need some crossing point)
// TODO what about multiple crossings? NOW we have undefined behavior / random crossing point is used
anyOtherGeometry = rendererPlaneGeometry;
if (m_LinkPlanes)
{
// if planes are linked, apply rotation to all planes
m_SNCsToBeRotated.push_back(snc);
}
}
else // close to cursor
{
if (nullptr == geometryToBeRotated) // first one close to the cursor
{
geometryToBeRotated = rendererPlaneGeometry;
intersectionLineWithGeometryToBeRotated = intersectionLine;
m_SNCsToBeRotated.push_back(snc);
}
else
{
// compare to the line defined by geometryToBeRotated: if identical, just rotate this otherRenderersRenderPlane
// together with the primary one
// if different, DON'T rotate
if (intersectionLine.IsParallel(intersectionLineWithGeometryToBeRotated)
&& intersectionLine.Distance(intersectionLineWithGeometryToBeRotated.GetPoint1()) < eps)
{
m_SNCsToBeRotated.push_back(snc);
}
else
{
hitMultipleLines = true;
}
}
}
}
bool moveSlices(true);
if (geometryToBeRotated && anyOtherGeometry && rendererWorldPlaneGeometry && !hitMultipleLines)
{
// assure all three are valid, so calculation of center of rotation can be done
moveSlices = false;
}
// question in state machine is: "rotate?"
if (moveSlices) // i.e. NOT rotate
{
return false;
}
else
{
// we have enough information for rotation
// remember where the last cursor position ON THE LINE has been observed
m_LastCursorPosition = intersectionLineWithGeometryToBeRotated.Project(position);
// find center of rotation by intersection with any of the OTHER lines
if (anyOtherGeometry->IntersectionPoint(intersectionLineWithGeometryToBeRotated, m_CenterOfRotation))
{
return true;
}
else
{
return false;
}
}
return false;
}
bool mitk::DisplayActionEventBroadcast::CheckSwivelPossible(const InteractionEvent *interactionEvent)
{
// Decide between moving and rotation: if we're close to the crossing
// point of the planes, moving mode is entered, otherwise
// rotation/swivel mode
const auto* positionEvent = dynamic_cast<const InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return false;
}
BaseRenderer* renderer = positionEvent->GetSender();
if (nullptr == renderer)
{
return false;
}
const Point3D& position = positionEvent->GetPositionInWorld();
m_SNCsToBeRotated.clear();
const PlaneGeometry* clickedGeometry(nullptr);
const PlaneGeometry* otherGeometry1(nullptr);
const PlaneGeometry* otherGeometry2(nullptr);
const ScalarType threshholdDistancePixels = 6.0;
auto allRenderWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows();
for (auto renderWindow : allRenderWindows)
{
SliceNavigationController* snc = BaseRenderer::GetInstance(renderWindow)->GetSliceNavigationController();
// If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes.
if (BaseRenderer::Standard3D == BaseRenderer::GetInstance(renderWindow)->GetMapperID())
{
continue;
}
const PlaneGeometry* rendererPlaneGeometry = snc->GetCurrentPlaneGeometry();
if (nullptr == rendererPlaneGeometry)
{
continue; // ignore, we don't see a plane
}
if (snc == renderer->GetSliceNavigationController())
{
clickedGeometry = rendererPlaneGeometry;
m_SNCsToBeRotated.push_back(snc);
}
else
{
if (nullptr == otherGeometry1)
{
otherGeometry1 = rendererPlaneGeometry;
}
else
{
otherGeometry2 = rendererPlaneGeometry;
}
if (m_LinkPlanes)
{
// if planes are linked, apply rotation to all planes
m_SNCsToBeRotated.push_back(snc);
}
}
}
Line3D line;
Point3D point;
if ((nullptr != clickedGeometry) && (nullptr != otherGeometry1) && (nullptr != otherGeometry2)
&& clickedGeometry->IntersectionLine(otherGeometry1, line) && otherGeometry2->IntersectionPoint(line, point))
{
m_CenterOfRotation = point;
if (m_CenterOfRotation.EuclideanDistanceTo(position) < threshholdDistancePixels)
{
return false;
}
else
{
m_ReferenceCursor = positionEvent->GetPointerPositionOnScreen();
// Get main axes of rotation plane and store it for rotation step
m_RotationPlaneNormal = clickedGeometry->GetNormal();
ScalarType xVector[] = { 1.0, 0.0, 0.0 };
ScalarType yVector[] = { 0.0, 1.0, 0.0 };
clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(xVector), m_RotationPlaneXVector);
clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(yVector), m_RotationPlaneYVector);
m_RotationPlaneNormal.Normalize();
m_RotationPlaneXVector.Normalize();
m_RotationPlaneYVector.Normalize();
m_PreviousRotationAxis.Fill(0.0);
m_PreviousRotationAxis[2] = 1.0;
m_PreviousRotationAngle = 0.0;
return true;
}
}
else
{
return false;
}
return false;
}
void mitk::DisplayActionEventBroadcast::Init(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent)
{
const auto* positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return;
}
m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
m_CurrentDisplayCoordinate = m_LastDisplayCoordinate;
positionEvent->GetSender()->DisplayToPlane(m_LastDisplayCoordinate, m_StartCoordinateInMM);
m_LastCoordinateInMM = m_StartCoordinateInMM;
}
void mitk::DisplayActionEventBroadcast::Move(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent)
{
const auto* positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return;
}
BaseRenderer* sender = interactionEvent->GetSender();
Vector2D moveVector = m_LastDisplayCoordinate - positionEvent->GetPointerPositionOnScreen();
if (m_InvertMoveDirection)
{
moveVector *= -1.0;
}
moveVector *= sender->GetScaleFactorMMPerDisplayUnit(); // #TODO: put here?
// store new display coordinate
m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
// propagate move event with computed geometry values
InvokeEvent(DisplayMoveEvent(interactionEvent, moveVector));
}
void mitk::DisplayActionEventBroadcast::SetCrosshair(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent)
{
const auto* positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return;
}
Point3D position = positionEvent->GetPositionInWorld();
// propagate set crosshair event with computed geometry values
InvokeEvent(DisplaySetCrosshairEvent(interactionEvent, position));
}
void mitk::DisplayActionEventBroadcast::Zoom(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent)
{
const auto* positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return;
}
float factor = 1.0;
float distance = 0;
if (m_ZoomDirection == "updown")
{
distance = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1];
}
else
{
distance = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0];
}
if (m_InvertZoomDirection)
{
distance *= -1.0;
}
// set zooming speed
if (distance < 0.0)
{
factor = 1.0 / m_ZoomFactor;
}
else if (distance > 0.0)
{
factor = 1.0 * m_ZoomFactor;
}
// store new display coordinates
m_LastDisplayCoordinate = m_CurrentDisplayCoordinate;
m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
// propagate zoom event with computed geometry values
InvokeEvent(DisplayZoomEvent(interactionEvent, factor, m_StartCoordinateInMM));
}
void mitk::DisplayActionEventBroadcast::Scroll(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent)
{
const auto* positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return;
}
int sliceDelta = 0;
// scroll direction
if (m_ScrollDirection == "updown")
{
sliceDelta = static_cast<int>(m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]);
}
else
{
sliceDelta = static_cast<int>(m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]);
}
if (m_InvertScrollDirection)
{
sliceDelta *= -1;
}
// set how many pixels the mouse has to be moved to scroll one slice
// if the mouse has been moved less than 'm_IndexToSliceModifier', pixels slice ONE slice only
if (sliceDelta > 0 && sliceDelta < m_IndexToSliceModifier)
{
sliceDelta = m_IndexToSliceModifier;
}
else if (sliceDelta < 0 && sliceDelta > -m_IndexToSliceModifier)
{
sliceDelta = -m_IndexToSliceModifier;
}
sliceDelta /= m_IndexToSliceModifier;
// store new display coordinates
m_LastDisplayCoordinate = m_CurrentDisplayCoordinate;
m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
// propagate scroll event with computed geometry values
InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta, m_AutoRepeat));
}
void mitk::DisplayActionEventBroadcast::ScrollOneUp(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent)
{
int sliceDelta = 1;
if (m_InvertScrollDirection)
{
sliceDelta = -1;
}
// propagate scroll event with a single slice delta (increase)
InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta, m_AutoRepeat));
}
void mitk::DisplayActionEventBroadcast::ScrollOneDown(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent)
{
int sliceDelta = -1;
if (m_InvertScrollDirection)
{
sliceDelta = 1;
}
// propagate scroll event with a single slice delta (decrease)
InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta, m_AutoRepeat));
}
void mitk::DisplayActionEventBroadcast::AdjustLevelWindow(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent)
{
const auto* positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return;
}
ScalarType level;
ScalarType window;
if (m_LevelDirection == "leftright")
{
level = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0];
window = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1];
}
else
{
level = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1];
window = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0];
}
if (m_InvertLevelWindowDirection)
{
level *= -1;
window *= -1;
}
level *= static_cast<ScalarType>(2);
window *= static_cast<ScalarType>(2);
// store new display coordinates
m_LastDisplayCoordinate = m_CurrentDisplayCoordinate;
m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
// propagate set level window event with the level and window delta
InvokeEvent(DisplaySetLevelWindowEvent(interactionEvent, level, window));
}
void mitk::DisplayActionEventBroadcast::StartRotation(StateMachineAction* /*stateMachineAction*/, InteractionEvent* /*interactionEvent*/)
{
SetMouseCursor(rotate_cursor_xpm, 0, 0);
}
void mitk::DisplayActionEventBroadcast::EndRotation(StateMachineAction* /*stateMachineAction*/, InteractionEvent* /*interactionEvent*/)
{
ResetMouseCursor();
}
void mitk::DisplayActionEventBroadcast::Rotate(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent)
{
const auto* positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return;
}
Point3D position = positionEvent->GetPositionInWorld();
Vector3D toProjected = m_LastCursorPosition - m_CenterOfRotation;
Vector3D toCursor = position - m_CenterOfRotation;
// cross product: | A x B | = |A| * |B| * sin(angle)
Vector3D axisOfRotation;
vnl_vector_fixed<ScalarType, 3> vnlDirection = vnl_cross_3d(toCursor.GetVnlVector(), toProjected.GetVnlVector());
- axisOfRotation.SetVnlVector(vnlDirection);
+ axisOfRotation.SetVnlVector(vnlDirection.as_ref());
// scalar product: A * B = |A| * |B| * cos(angle)
// tan = sin / cos
ScalarType angle = -atan2((double)(axisOfRotation.GetNorm()), (double)(toCursor * toProjected));
angle *= 180.0 / vnl_math::pi;
m_LastCursorPosition = position;
// create RotationOperation and apply to all SNCs that should be rotated
RotationOperation rotationOperation(OpROTATE, m_CenterOfRotation, axisOfRotation, angle);
// iterate the OTHER slice navigation controllers
for (auto iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter)
{
TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry();
if (nullptr == timeGeometry)
{
continue;
}
timeGeometry->ExecuteOperation(&rotationOperation);
(*iter)->SendCreatedWorldGeometryUpdate();
}
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::DisplayActionEventBroadcast::Swivel(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent)
{
const auto* positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return;
}
// Determine relative mouse movement projected onto world space
Point2D position = positionEvent->GetPointerPositionOnScreen();
Vector2D relativeCursor = position - m_ReferenceCursor;
Vector3D relativeCursorAxis = m_RotationPlaneXVector * relativeCursor[0] + m_RotationPlaneYVector * relativeCursor[1];
// Determine rotation axis (perpendicular to rotation plane and cursor movement)
Vector3D rotationAxis = itk::CrossProduct(m_RotationPlaneNormal, relativeCursorAxis);
ScalarType rotationAngle = relativeCursor.GetNorm() / 2.0;
// Restore the initial plane pose by undoing the previous rotation operation
RotationOperation op(OpROTATE, m_CenterOfRotation, m_PreviousRotationAxis, -m_PreviousRotationAngle);
SNCVector::iterator iter;
for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter)
{
if (!(*iter)->GetSliceRotationLocked())
{
TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry();
if (nullptr == timeGeometry)
{
continue;
}
timeGeometry->ExecuteOperation(&op);
(*iter)->SendCreatedWorldGeometryUpdate();
}
}
// Apply new rotation operation to all relevant SNCs
RotationOperation op2(OpROTATE, m_CenterOfRotation, rotationAxis, rotationAngle);
for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter)
{
if (!(*iter)->GetSliceRotationLocked())
{
// Retrieve the TimeGeometry of this SliceNavigationController
TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry();
if (nullptr == timeGeometry)
{
continue;
}
// Execute the new rotation
timeGeometry->ExecuteOperation(&op2);
// Notify listeners
(*iter)->SendCreatedWorldGeometryUpdate();
}
}
m_PreviousRotationAxis = rotationAxis;
m_PreviousRotationAngle = rotationAngle;
RenderingManager::GetInstance()->RequestUpdateAll();
return;
}
void mitk::DisplayActionEventBroadcast::IncreaseTimeStep(StateMachineAction*, InteractionEvent*)
{
auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController();
auto stepper = sliceNaviController->GetTime();
stepper->SetAutoRepeat(true);
stepper->Next();
}
void mitk::DisplayActionEventBroadcast::DecreaseTimeStep(StateMachineAction*, InteractionEvent*)
{
auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController();
auto stepper = sliceNaviController->GetTime();
stepper->SetAutoRepeat(true);
stepper->Previous();
}
void mitk::DisplayActionEventBroadcast::UpdateStatusbar(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent)
{
const auto* positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return;
}
BaseRenderer::Pointer renderer = positionEvent->GetSender();
TNodePredicateDataType<Image>::Pointer isImageData = TNodePredicateDataType<Image>::New();
DataStorage::SetOfObjects::ConstPointer nodes = renderer->GetDataStorage()->GetSubset(isImageData).GetPointer();
if (nodes.IsNull())
{
return;
}
Point3D worldposition;
renderer->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), worldposition);
auto globalCurrentTimePoint = renderer->GetTime();
Image::Pointer image3D;
DataNode::Pointer node;
DataNode::Pointer topSourceNode;
int component = 0;
node = FindTopmostVisibleNode(nodes, worldposition, globalCurrentTimePoint, renderer);
if (node.IsNull())
{
return;
}
bool isBinary(false);
node->GetBoolProperty("binary", isBinary);
if (isBinary)
{
DataStorage::SetOfObjects::ConstPointer sourcenodes = renderer->GetDataStorage()->GetSources(node, nullptr, true);
if (!sourcenodes->empty())
{
topSourceNode = FindTopmostVisibleNode(nodes, worldposition, globalCurrentTimePoint, renderer);
}
if (topSourceNode.IsNotNull())
{
image3D = dynamic_cast<Image*>(topSourceNode->GetData());
topSourceNode->GetIntProperty("Image.Displayed Component", component);
}
else
{
image3D = dynamic_cast<Image*>(node->GetData());
node->GetIntProperty("Image.Displayed Component", component);
}
}
else
{
image3D = dynamic_cast<Image *>(node->GetData());
node->GetIntProperty("Image.Displayed Component", component);
}
// get the position and pixel value from the image and build up status bar text
auto statusBar = StatusBar::GetInstance();
if (image3D.IsNotNull() && statusBar != nullptr)
{
itk::Index<3> p;
image3D->GetGeometry()->WorldToIndex(worldposition, p);
auto pixelType = image3D->GetChannelDescriptor().GetPixelType().GetPixelType();
- if (pixelType == itk::ImageIOBase::RGB || pixelType == itk::ImageIOBase::RGBA)
+ if (pixelType == itk::IOPixelEnum::RGB || pixelType == itk::IOPixelEnum::RGBA)
{
std::string pixelValue = "Pixel RGB(A) value: ";
pixelValue.append(ConvertCompositePixelValueToString(image3D, p));
statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue.c_str());
}
- else if (pixelType == itk::ImageIOBase::DIFFUSIONTENSOR3D || pixelType == itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR)
+ else if (pixelType == itk::IOPixelEnum::DIFFUSIONTENSOR3D || pixelType == itk::IOPixelEnum::SYMMETRICSECONDRANKTENSOR)
{
std::string pixelValue = "See ODF Details view. ";
statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue.c_str());
}
else
{
ScalarType pixelValue;
mitkPixelTypeMultiplex5(
FastSinglePixelAccess,
image3D->GetChannelDescriptor().GetPixelType(),
image3D,
image3D->GetVolumeData(renderer->GetTimeStep()),
p,
pixelValue,
component);
statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue);
}
}
else
{
statusBar->DisplayImageInfoInvalid();
}
}
bool mitk::DisplayActionEventBroadcast::GetBoolProperty(PropertyList::Pointer propertyList, const char* propertyName, bool defaultValue)
{
std::string valueAsString;
if (!propertyList->GetStringProperty(propertyName, valueAsString))
{
return defaultValue;
}
else
{
if (valueAsString == "true")
{
return true;
}
else
{
return false;
}
}
}
diff --git a/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp b/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp
index bb6a9ae2d8..def8774def 100644
--- a/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp
+++ b/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp
@@ -1,944 +1,944 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkDisplayInteractor.h"
#include "mitkBaseRenderer.h"
#include "mitkCameraController.h"
#include "mitkInteractionPositionEvent.h"
#include "mitkPropertyList.h"
#include <mitkAbstractTransformGeometry.h>
#include <mitkRotationOperation.h>
#include <cstring>
// level window
#include "mitkLevelWindow.h"
#include "mitkLevelWindowProperty.h"
#include "mitkLine.h"
#include "mitkNodePredicateDataType.h"
#include "mitkStandaloneDataStorage.h"
#include "vtkRenderWindowInteractor.h"
// Rotation
#include "mitkInteractionConst.h"
#include "rotate_cursor.xpm"
#include <mitkImagePixelReadAccessor.h>
#include <mitkRotationOperation.h>
#include "mitkImage.h"
#include "mitkImagePixelReadAccessor.h"
#include "mitkPixelTypeMultiplex.h"
#include "mitkStatusBar.h"
#include <mitkCompositePixelValueToString.h>
void mitk::DisplayInteractor::Notify(InteractionEvent *interactionEvent, bool isHandled)
{
// to use the state machine pattern,
// the event is passed to the state machine interface to be handled
if (!isHandled || m_AlwaysReact)
{
HandleEvent(interactionEvent, nullptr);
}
}
mitk::DisplayInteractor::DisplayInteractor()
: m_IndexToSliceModifier(4)
, m_AutoRepeat(false)
, m_InvertScrollDirection(false)
, m_InvertZoomDirection(false)
, m_InvertMoveDirection(false)
, m_InvertLevelWindowDirection(false)
, m_AlwaysReact(false)
, m_ZoomFactor(2)
, m_LinkPlanes(true)
{
m_StartCoordinateInMM.Fill(0);
m_LastDisplayCoordinate.Fill(0);
m_LastCoordinateInMM.Fill(0);
m_CurrentDisplayCoordinate.Fill(0);
}
mitk::DisplayInteractor::~DisplayInteractor()
{
// nothing here
}
void mitk::DisplayInteractor::ConnectActionsAndFunctions()
{
CONNECT_CONDITION("check_position_event", CheckPositionEvent);
CONNECT_CONDITION("check_can_rotate", CheckRotationPossible);
CONNECT_CONDITION("check_can_swivel", CheckSwivelPossible);
CONNECT_FUNCTION("init", Init);
CONNECT_FUNCTION("move", Move);
CONNECT_FUNCTION("zoom", Zoom);
CONNECT_FUNCTION("scroll", Scroll);
CONNECT_FUNCTION("ScrollOneDown", ScrollOneDown);
CONNECT_FUNCTION("ScrollOneUp", ScrollOneUp);
CONNECT_FUNCTION("levelWindow", AdjustLevelWindow);
CONNECT_FUNCTION("setCrosshair", SetCrosshair);
CONNECT_FUNCTION("updateStatusbar", UpdateStatusbar)
CONNECT_FUNCTION("startRotation", StartRotation);
CONNECT_FUNCTION("endRotation", EndRotation);
CONNECT_FUNCTION("rotate", Rotate);
CONNECT_FUNCTION("swivel", Swivel);
CONNECT_FUNCTION("IncreaseTimeStep", IncreaseTimeStep);
CONNECT_FUNCTION("DecreaseTimeStep", DecreaseTimeStep);
}
bool mitk::DisplayInteractor::CheckPositionEvent(const InteractionEvent *interactionEvent)
{
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
{
return false;
}
return true;
}
bool mitk::DisplayInteractor::CheckRotationPossible(const mitk::InteractionEvent *interactionEvent)
{
// Decide between moving and rotation slices.
/*
Detailed logic:
1. Find the SliceNavigationController that has sent the event: this one defines our rendering plane and will NOT be
rotated. Needs not even be counted or checked.
2. Inspect every other SliceNavigationController
- calculate the line intersection of this SliceNavigationController's plane with our rendering plane
- if there is NO interesection, ignore and continue
- IF there is an intersection
- check the mouse cursor's distance from that line.
0. if the line is NOT near the cursor, remember the plane as "one of the other planes" (which can be rotated in
"locked" mode)
1. on first line near the cursor, just remember this intersection line as THE other plane that we want to rotate
2. on every consecutive line near the cursor, check if the line is geometrically identical to the line that we want to
rotate
- if yes, we just push this line to the "other" lines and rotate it along
- if no, then we have a situation where the mouse is near two other lines (e.g. crossing point) and don't want to
rotate
*/
const auto *posEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (posEvent == nullptr)
return false;
BaseRenderer *clickedRenderer = posEvent->GetSender();
const PlaneGeometry *ourViewportGeometry = (clickedRenderer->GetCurrentWorldPlaneGeometry());
if (!ourViewportGeometry)
return false;
Point3D cursorPosition = posEvent->GetPositionInWorld();
const auto spacing = ourViewportGeometry->GetSpacing();
const PlaneGeometry *geometryToBeRotated = nullptr; // this one is under the mouse cursor
const PlaneGeometry *anyOtherGeometry = nullptr; // this is also visible (for calculation of intersection ONLY)
Line3D intersectionLineWithGeometryToBeRotated;
bool hitMultipleLines(false);
m_SNCsToBeRotated.clear();
const double threshholdDistancePixels = 12.0;
auto renWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows();
for (auto renWin : renWindows)
{
SliceNavigationController *snc = BaseRenderer::GetInstance(renWin)->GetSliceNavigationController();
// If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes.
if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D)
continue;
const PlaneGeometry *otherRenderersRenderPlane = snc->GetCurrentPlaneGeometry();
if (otherRenderersRenderPlane == nullptr)
continue; // ignore, we don't see a plane
// check if there is an intersection
Line3D intersectionLine; // between rendered/clicked geometry and the one being analyzed
if (!ourViewportGeometry->IntersectionLine(otherRenderersRenderPlane, intersectionLine))
{
continue; // we ignore this plane, it's parallel to our plane
}
// check distance from intersection line
const double distanceFromIntersectionLine =
intersectionLine.Distance(cursorPosition) / spacing[snc->GetDefaultViewDirection()];
// far away line, only remember for linked rotation if necessary
if (distanceFromIntersectionLine > threshholdDistancePixels)
{
anyOtherGeometry = otherRenderersRenderPlane; // we just take the last one, so overwrite each iteration (we just
// need some crossing point)
// TODO what about multiple crossings? NOW we have undefined behavior / random crossing point is used
if (m_LinkPlanes)
{
m_SNCsToBeRotated.push_back(snc);
}
}
else // close to cursor
{
if (geometryToBeRotated == nullptr) // first one close to the cursor
{
geometryToBeRotated = otherRenderersRenderPlane;
intersectionLineWithGeometryToBeRotated = intersectionLine;
m_SNCsToBeRotated.push_back(snc);
}
else
{
// compare to the line defined by geometryToBeRotated: if identical, just rotate this otherRenderersRenderPlane
// together with the primary one
// if different, DON'T rotate
if (intersectionLine.IsParallel(intersectionLineWithGeometryToBeRotated) &&
intersectionLine.Distance(intersectionLineWithGeometryToBeRotated.GetPoint1()) < mitk::eps)
{
m_SNCsToBeRotated.push_back(snc);
}
else
{
hitMultipleLines = true;
}
}
}
}
bool moveSlices(true);
if (geometryToBeRotated && anyOtherGeometry && ourViewportGeometry && !hitMultipleLines)
{
// assure all three are valid, so calculation of center of rotation can be done
moveSlices = false;
}
// question in state machine is: "rotate?"
if (moveSlices) // i.e. NOT rotate
{
return false;
}
else
{ // we DO have enough information for rotation
m_LastCursorPosition = intersectionLineWithGeometryToBeRotated.Project(
cursorPosition); // remember where the last cursor position ON THE LINE has been observed
if (anyOtherGeometry->IntersectionPoint(
intersectionLineWithGeometryToBeRotated,
m_CenterOfRotation)) // find center of rotation by intersection with any of the OTHER lines
{
return true;
}
else
{
return false;
}
}
return false;
}
bool mitk::DisplayInteractor::CheckSwivelPossible(const mitk::InteractionEvent *interactionEvent)
{
const ScalarType ThresholdDistancePixels = 6.0;
// Decide between moving and rotation: if we're close to the crossing
// point of the planes, moving mode is entered, otherwise
// rotation/swivel mode
const auto *posEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
BaseRenderer *renderer = interactionEvent->GetSender();
if (!posEvent || !renderer)
return false;
const Point3D &cursor = posEvent->GetPositionInWorld();
m_SNCsToBeRotated.clear();
const PlaneGeometry *clickedGeometry(nullptr);
const PlaneGeometry *otherGeometry1(nullptr);
const PlaneGeometry *otherGeometry2(nullptr);
auto renWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows();
for (auto renWin : renWindows)
{
SliceNavigationController *snc = BaseRenderer::GetInstance(renWin)->GetSliceNavigationController();
// If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes.
if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D)
continue;
const PlaneGeometry *planeGeometry = snc->GetCurrentPlaneGeometry();
if (!planeGeometry)
continue;
if (snc == renderer->GetSliceNavigationController())
{
clickedGeometry = planeGeometry;
m_SNCsToBeRotated.push_back(snc);
}
else
{
if (otherGeometry1 == nullptr)
{
otherGeometry1 = planeGeometry;
}
else
{
otherGeometry2 = planeGeometry;
}
if (m_LinkPlanes)
{
// If planes are linked, apply rotation to all planes
m_SNCsToBeRotated.push_back(snc);
}
}
}
mitk::Line3D line;
mitk::Point3D point;
if ((clickedGeometry != nullptr) && (otherGeometry1 != nullptr) && (otherGeometry2 != nullptr) &&
clickedGeometry->IntersectionLine(otherGeometry1, line) && otherGeometry2->IntersectionPoint(line, point))
{
m_CenterOfRotation = point;
if (m_CenterOfRotation.EuclideanDistanceTo(cursor) < ThresholdDistancePixels)
{
return false;
}
else
{
m_ReferenceCursor = posEvent->GetPointerPositionOnScreen();
// Get main axes of rotation plane and store it for rotation step
m_RotationPlaneNormal = clickedGeometry->GetNormal();
ScalarType xVector[] = {1.0, 0.0, 0.0};
ScalarType yVector[] = {0.0, 1.0, 0.0};
clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(xVector), m_RotationPlaneXVector);
clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(yVector), m_RotationPlaneYVector);
m_RotationPlaneNormal.Normalize();
m_RotationPlaneXVector.Normalize();
m_RotationPlaneYVector.Normalize();
m_PreviousRotationAxis.Fill(0.0);
m_PreviousRotationAxis[2] = 1.0;
m_PreviousRotationAngle = 0.0;
return true;
}
}
else
{
return false;
}
return false;
}
void mitk::DisplayInteractor::Init(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto *positionEvent = static_cast<InteractionPositionEvent *>(interactionEvent);
m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
m_CurrentDisplayCoordinate = m_LastDisplayCoordinate;
positionEvent->GetSender()->DisplayToPlane(m_LastDisplayCoordinate, m_StartCoordinateInMM);
m_LastCoordinateInMM = m_StartCoordinateInMM;
}
void mitk::DisplayInteractor::Move(StateMachineAction *, InteractionEvent *interactionEvent)
{
BaseRenderer *sender = interactionEvent->GetSender();
auto *positionEvent = static_cast<InteractionPositionEvent *>(interactionEvent);
float invertModifier = -1.0;
if (m_InvertMoveDirection)
{
invertModifier = 1.0;
}
// perform translation
Vector2D moveVector = (positionEvent->GetPointerPositionOnScreen() - m_LastDisplayCoordinate) * invertModifier;
moveVector *= sender->GetScaleFactorMMPerDisplayUnit();
sender->GetCameraController()->MoveBy(moveVector);
RenderingManager::GetInstance()->RequestUpdate(sender->GetRenderWindow());
m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
}
void mitk::DisplayInteractor::SetCrosshair(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent)
{
auto* positionEvent = static_cast<InteractionPositionEvent*>(interactionEvent);
Point3D pos = positionEvent->GetPositionInWorld();
const BaseRenderer::Pointer sender = interactionEvent->GetSender();
auto renWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows();
for (auto renWin : renWindows)
{
if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard2D && renWin != sender->GetRenderWindow())
{
BaseRenderer::GetInstance(renWin)->GetSliceNavigationController()->SelectSliceByPoint(pos);
}
}
}
void mitk::DisplayInteractor::IncreaseTimeStep(StateMachineAction *, InteractionEvent *)
{
auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController();
auto stepper = sliceNaviController->GetTime();
stepper->SetAutoRepeat(true);
stepper->Next();
}
void mitk::DisplayInteractor::DecreaseTimeStep(StateMachineAction *, InteractionEvent *)
{
auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController();
auto stepper = sliceNaviController->GetTime();
stepper->SetAutoRepeat(true);
stepper->Previous();
}
void mitk::DisplayInteractor::Zoom(StateMachineAction *, InteractionEvent *interactionEvent)
{
float factor = 1.0;
float distance = 0;
if (m_ZoomDirection == "updown")
{
distance = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1];
}
else
{
distance = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0];
}
if (m_InvertZoomDirection)
{
distance *= -1.0;
}
// set zooming speed
if (distance < 0.0)
{
factor = 1.0 / m_ZoomFactor;
}
else if (distance > 0.0)
{
factor = 1.0 * m_ZoomFactor;
}
auto* positionEvent = static_cast<InteractionPositionEvent*>(interactionEvent);
m_LastDisplayCoordinate = m_CurrentDisplayCoordinate;
m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
if (factor != 1.0)
{
const BaseRenderer::Pointer sender = interactionEvent->GetSender();
sender->GetCameraController()->Zoom(factor, m_StartCoordinateInMM);
RenderingManager::GetInstance()->RequestUpdate(sender->GetRenderWindow());
}
}
void mitk::DisplayInteractor::Scroll(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto* positionEvent = static_cast<InteractionPositionEvent *>(interactionEvent);
mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController();
if (sliceNaviController)
{
int delta = 0;
// Scrolling direction
if (m_ScrollDirection == "updown")
{
delta = static_cast<int>(m_LastDisplayCoordinate[1] - positionEvent->GetPointerPositionOnScreen()[1]);
}
else
{
delta = static_cast<int>(m_LastDisplayCoordinate[0] - positionEvent->GetPointerPositionOnScreen()[0]);
}
if (m_InvertScrollDirection)
{
delta *= -1;
}
// Set how many pixels the mouse has to be moved to scroll one slice
// if we moved less than 'm_IndexToSliceModifier' pixels slice ONE slice only
if (delta > 0 && delta < m_IndexToSliceModifier)
{
delta = m_IndexToSliceModifier;
}
else if (delta < 0 && delta > -m_IndexToSliceModifier)
{
delta = -m_IndexToSliceModifier;
}
delta /= m_IndexToSliceModifier;
int newPos = sliceNaviController->GetSlice()->GetPos() + delta;
// if auto repeat is on, start at first slice if you reach the last slice and vice versa
int maxSlices = sliceNaviController->GetSlice()->GetSteps();
if (m_AutoRepeat)
{
while (newPos < 0)
{
newPos += maxSlices;
}
while (newPos >= maxSlices)
{
newPos -= maxSlices;
}
}
else
{
// if the new slice is below 0 we still show slice 0
// due to the stepper using unsigned int we have to do this ourselves
if (newPos < 1)
{
newPos = 0;
}
}
m_LastDisplayCoordinate = m_CurrentDisplayCoordinate;
m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
// set the new position
sliceNaviController->GetSlice()->SetPos(newPos);
}
}
void mitk::DisplayInteractor::ScrollOneDown(StateMachineAction *, InteractionEvent *interactionEvent)
{
mitk::SliceNavigationController::Pointer sliceNaviController =
interactionEvent->GetSender()->GetSliceNavigationController();
if (!sliceNaviController->GetSliceLocked())
{
mitk::Stepper *stepper = sliceNaviController->GetSlice();
if (stepper->GetSteps() <= 1)
{
stepper = sliceNaviController->GetTime();
}
stepper->Next();
}
}
void mitk::DisplayInteractor::ScrollOneUp(StateMachineAction *, InteractionEvent *interactionEvent)
{
mitk::SliceNavigationController::Pointer sliceNaviController =
interactionEvent->GetSender()->GetSliceNavigationController();
if (!sliceNaviController->GetSliceLocked())
{
mitk::Stepper *stepper = sliceNaviController->GetSlice();
if (stepper->GetSteps() <= 1)
{
stepper = sliceNaviController->GetTime();
}
stepper->Previous();
}
}
void mitk::DisplayInteractor::AdjustLevelWindow(StateMachineAction *, InteractionEvent *interactionEvent)
{
BaseRenderer::Pointer sender = interactionEvent->GetSender();
auto *positionEvent = static_cast<InteractionPositionEvent *>(interactionEvent);
m_LastDisplayCoordinate = m_CurrentDisplayCoordinate;
m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen();
// search for active image
mitk::DataStorage::Pointer storage = sender->GetDataStorage();
mitk::DataNode::Pointer node = nullptr;
mitk::DataStorage::SetOfObjects::ConstPointer allImageNodes = storage->GetSubset(mitk::NodePredicateDataType::New("Image"));
for (unsigned int i = 0; i < allImageNodes->size(); ++i)
{
bool isActiveImage = false;
bool propFound = allImageNodes->at(i)->GetBoolProperty("imageForLevelWindow", isActiveImage);
if (propFound && isActiveImage)
{
node = allImageNodes->at(i);
continue;
}
}
if (node.IsNull())
{
node = storage->GetNode(mitk::NodePredicateDataType::New("Image"));
}
if (node.IsNull())
{
return;
}
mitk::LevelWindow lv = mitk::LevelWindow();
node->GetLevelWindow(lv);
ScalarType level = lv.GetLevel();
ScalarType window = lv.GetWindow();
int levelIndex = 0;
int windowIndex = 1;
if (m_LevelDirection != "leftright")
{
levelIndex = 1;
windowIndex = 0;
}
int directionModifier = 1;
if (m_InvertLevelWindowDirection)
{
directionModifier = -1;
}
// calculate adjustments from mouse movements
level += (m_CurrentDisplayCoordinate[levelIndex] - m_LastDisplayCoordinate[levelIndex]) * static_cast<ScalarType>(2) *
directionModifier;
window += (m_CurrentDisplayCoordinate[windowIndex] - m_LastDisplayCoordinate[windowIndex]) *
static_cast<ScalarType>(2) * directionModifier;
lv.SetLevelWindow(level, window);
dynamic_cast<mitk::LevelWindowProperty *>(node->GetProperty("levelwindow"))->SetLevelWindow(lv);
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::DisplayInteractor::StartRotation(mitk::StateMachineAction *, mitk::InteractionEvent *)
{
this->SetMouseCursor(rotate_cursor_xpm, 0, 0);
}
void mitk::DisplayInteractor::EndRotation(mitk::StateMachineAction *, mitk::InteractionEvent *)
{
this->ResetMouseCursor();
}
void mitk::DisplayInteractor::Rotate(mitk::StateMachineAction *, mitk::InteractionEvent *event)
{
const auto *posEvent = dynamic_cast<const InteractionPositionEvent *>(event);
if (posEvent == nullptr)
return;
Point3D cursor = posEvent->GetPositionInWorld();
Vector3D toProjected = m_LastCursorPosition - m_CenterOfRotation;
Vector3D toCursor = cursor - m_CenterOfRotation;
// cross product: | A x B | = |A| * |B| * sin(angle)
Vector3D axisOfRotation;
vnl_vector_fixed<ScalarType, 3> vnlDirection = vnl_cross_3d(toCursor.GetVnlVector(), toProjected.GetVnlVector());
- axisOfRotation.SetVnlVector(vnlDirection);
+ axisOfRotation.SetVnlVector(vnlDirection.as_ref());
// scalar product: A * B = |A| * |B| * cos(angle)
// tan = sin / cos
ScalarType angle = -atan2((double)(axisOfRotation.GetNorm()), (double)(toCursor * toProjected));
angle *= 180.0 / vnl_math::pi;
m_LastCursorPosition = cursor;
// create RotationOperation and apply to all SNCs that should be rotated
RotationOperation rotationOperation(OpROTATE, m_CenterOfRotation, axisOfRotation, angle);
// iterate the OTHER slice navigation controllers: these are filled in DoDecideBetweenRotationAndSliceSelection
for (auto iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter)
{
TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry();
if (!timeGeometry)
continue;
timeGeometry->ExecuteOperation(&rotationOperation);
(*iter)->SendCreatedWorldGeometryUpdate();
}
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::DisplayInteractor::Swivel(mitk::StateMachineAction *, mitk::InteractionEvent *event)
{
const auto *posEvent = dynamic_cast<const InteractionPositionEvent *>(event);
if (!posEvent)
return;
// Determine relative mouse movement projected onto world space
Point2D cursor = posEvent->GetPointerPositionOnScreen();
Vector2D relativeCursor = cursor - m_ReferenceCursor;
Vector3D relativeCursorAxis = m_RotationPlaneXVector * relativeCursor[0] + m_RotationPlaneYVector * relativeCursor[1];
// Determine rotation axis (perpendicular to rotation plane and cursor
// movement)
Vector3D rotationAxis = itk::CrossProduct(m_RotationPlaneNormal, relativeCursorAxis);
ScalarType rotationAngle = relativeCursor.GetNorm() / 2.0;
// Restore the initial plane pose by undoing the previous rotation
// operation
RotationOperation op(OpROTATE, m_CenterOfRotation, m_PreviousRotationAxis, -m_PreviousRotationAngle);
SNCVector::iterator iter;
for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter)
{
if (!(*iter)->GetSliceRotationLocked())
{
TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry();
if (!timeGeometry)
continue;
timeGeometry->ExecuteOperation(&op);
(*iter)->SendCreatedWorldGeometryUpdate();
}
}
// Apply new rotation operation to all relevant SNCs
RotationOperation op2(OpROTATE, m_CenterOfRotation, rotationAxis, rotationAngle);
for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter)
{
if (!(*iter)->GetSliceRotationLocked())
{
// Retrieve the TimeGeometry of this SliceNavigationController
TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry();
if (!timeGeometry)
continue;
// Execute the new rotation
timeGeometry->ExecuteOperation(&op2);
// Notify listeners
(*iter)->SendCreatedWorldGeometryUpdate();
}
}
m_PreviousRotationAxis = rotationAxis;
m_PreviousRotationAngle = rotationAngle;
RenderingManager::GetInstance()->RequestUpdateAll();
return;
}
void mitk::DisplayInteractor::UpdateStatusbar(mitk::StateMachineAction *, mitk::InteractionEvent *event)
{
const auto* posEvent = dynamic_cast<const InteractionPositionEvent *>(event);
if (nullptr == posEvent)
{
return;
}
const mitk::BaseRenderer::Pointer baseRenderer = posEvent->GetSender();
TNodePredicateDataType<mitk::Image>::Pointer isImageData = TNodePredicateDataType<mitk::Image>::New();
auto globalCurrentTimePoint = baseRenderer->GetTime();
mitk::DataStorage::SetOfObjects::ConstPointer nodes = baseRenderer->GetDataStorage()->GetSubset(isImageData).GetPointer();
if (nodes.IsNull())
{
return;
}
// posEvent->GetPositionInWorld() would return the world position at the
// time of initiating the interaction. However, we need to update the
// status bar with the position after changing slice. Therefore, we
// translate the same display position with the renderer again to
// get the new world position.
Point3D worldposition;
baseRenderer->DisplayToWorld(posEvent->GetPointerPositionOnScreen(), worldposition);
mitk::Image::Pointer image3D;
mitk::DataNode::Pointer node;
mitk::DataNode::Pointer topSourceNode;
int component = 0;
node = FindTopmostVisibleNode(nodes, worldposition, globalCurrentTimePoint, baseRenderer);
if (node.IsNull())
{
return;
}
bool isBinary(false);
node->GetBoolProperty("binary", isBinary);
if (isBinary)
{
mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = baseRenderer->GetDataStorage()->GetSources(node, nullptr, true);
if (!sourcenodes->empty())
{
topSourceNode = mitk::FindTopmostVisibleNode(sourcenodes, worldposition, globalCurrentTimePoint, baseRenderer);
}
if (topSourceNode.IsNotNull())
{
image3D = dynamic_cast<mitk::Image *>(topSourceNode->GetData());
topSourceNode->GetIntProperty("Image.Displayed Component", component);
}
else
{
image3D = dynamic_cast<mitk::Image *>(node->GetData());
node->GetIntProperty("Image.Displayed Component", component);
}
}
else
{
image3D = dynamic_cast<mitk::Image *>(node->GetData());
node->GetIntProperty("Image.Displayed Component", component);
}
// get the position and gray value from the image and build up status bar text
auto statusBar = StatusBar::GetInstance();
if (image3D.IsNotNull() && statusBar != nullptr)
{
itk::Index<3> p;
image3D->GetGeometry()->WorldToIndex(worldposition, p);
auto pixelType = image3D->GetChannelDescriptor().GetPixelType().GetPixelType();
- if (pixelType == itk::ImageIOBase::RGB || pixelType == itk::ImageIOBase::RGBA)
+ if (pixelType == itk::IOPixelEnum::RGB || pixelType == itk::IOPixelEnum::RGBA)
{
std::string pixelValue = "Pixel RGB(A) value: ";
pixelValue.append(ConvertCompositePixelValueToString(image3D, p));
statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, pixelValue.c_str());
}
- else if (pixelType == itk::ImageIOBase::DIFFUSIONTENSOR3D || pixelType == itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR)
+ else if (pixelType == itk::IOPixelEnum::DIFFUSIONTENSOR3D || pixelType == itk::IOPixelEnum::SYMMETRICSECONDRANKTENSOR)
{
std::string pixelValue = "See ODF Details view. ";
statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, pixelValue.c_str());
}
else
{
mitk::ScalarType pixelValue;
mitkPixelTypeMultiplex5(mitk::FastSinglePixelAccess,
image3D->GetChannelDescriptor().GetPixelType(),
image3D,
image3D->GetVolumeData(image3D->GetTimeGeometry()->TimePointToTimeStep(globalCurrentTimePoint)),
p,
pixelValue,
component);
statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, pixelValue);
}
}
else
{
statusBar->DisplayImageInfoInvalid();
}
}
void mitk::DisplayInteractor::ConfigurationChanged()
{
mitk::PropertyList::Pointer properties = GetAttributes();
// auto repeat
std::string strAutoRepeat = "";
if (properties->GetStringProperty("autoRepeat", strAutoRepeat))
{
if (strAutoRepeat == "true")
{
m_AutoRepeat = true;
}
else
{
m_AutoRepeat = false;
}
}
// pixel movement for scrolling one slice
std::string strPixelPerSlice = "";
if (properties->GetStringProperty("pixelPerSlice", strPixelPerSlice))
{
m_IndexToSliceModifier = atoi(strPixelPerSlice.c_str());
}
else
{
m_IndexToSliceModifier = 4;
}
// scroll direction
if (!properties->GetStringProperty("scrollDirection", m_ScrollDirection))
{
m_ScrollDirection = "updown";
}
m_InvertScrollDirection = GetBoolProperty(properties, "invertScrollDirection", false);
// zoom direction
if (!properties->GetStringProperty("zoomDirection", m_ZoomDirection))
{
m_ZoomDirection = "updown";
}
m_InvertZoomDirection = GetBoolProperty(properties, "invertZoomDirection", false);
m_InvertMoveDirection = GetBoolProperty(properties, "invertMoveDirection", false);
if (!properties->GetStringProperty("levelWindowDirection", m_LevelDirection))
{
m_LevelDirection = "leftright";
}
m_InvertLevelWindowDirection = GetBoolProperty(properties, "invertLevelWindowDirection", false);
// coupled rotation
std::string strCoupled = "";
if (properties->GetStringProperty("coupled", strCoupled))
{
if (strCoupled == "true")
m_LinkPlanes = true;
else
m_LinkPlanes = false;
}
// zoom factor
std::string strZoomFactor = "";
properties->GetStringProperty("zoomFactor", strZoomFactor);
m_ZoomFactor = .05;
if (atoi(strZoomFactor.c_str()) > 0)
{
m_ZoomFactor = 1.0 + (atoi(strZoomFactor.c_str()) / 100.0);
}
// allwaysReact
std::string strAlwaysReact = "";
if (properties->GetStringProperty("alwaysReact", strAlwaysReact))
{
if (strAlwaysReact == "true")
{
m_AlwaysReact = true;
}
else
{
m_AlwaysReact = false;
}
}
else
{
m_AlwaysReact = false;
}
}
bool mitk::DisplayInteractor::FilterEvents(InteractionEvent *interactionEvent, DataNode * /*dataNode*/)
{
if (interactionEvent->GetSender() == nullptr)
return false;
if (interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard3D)
return false;
return true;
}
bool mitk::DisplayInteractor::GetBoolProperty(mitk::PropertyList::Pointer propertyList,
const char *propertyName,
bool defaultValue)
{
std::string valueAsString;
if (!propertyList->GetStringProperty(propertyName, valueAsString))
{
return defaultValue;
}
else
{
if (valueAsString == "true")
{
return true;
}
else
{
return false;
}
}
}
diff --git a/Modules/Core/src/Interactions/mitkInteractionSchemeSwitcher.cpp b/Modules/Core/src/Interactions/mitkInteractionSchemeSwitcher.cpp
index dc9f1fa6ab..47cc81ee04 100644
--- a/Modules/Core/src/Interactions/mitkInteractionSchemeSwitcher.cpp
+++ b/Modules/Core/src/Interactions/mitkInteractionSchemeSwitcher.cpp
@@ -1,108 +1,113 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkInteractionSchemeSwitcher.h"
// mitk core
#include <mitkInteractionEventObserver.h>
#include <mitkExceptionMacro.h>
+namespace mitk
+{
+ itkEventMacroDefinition(InteractionSchemeChangedEvent, itk::AnyEvent);
+}
+
mitk::InteractionSchemeSwitcher::InteractionSchemeSwitcher()
{
// nothing here
}
mitk::InteractionSchemeSwitcher::~InteractionSchemeSwitcher()
{
// nothing here
}
void mitk::InteractionSchemeSwitcher::SetInteractionScheme(InteractionEventHandler* interactionEventHandler, InteractionScheme interactionScheme)
{
if (nullptr == interactionEventHandler)
{
mitkThrow() << "Not a valid interaction event handler to set the interaction scheme.";
}
switch (interactionScheme)
{
// MITK MODE
case MITKStandard:
{
interactionEventHandler->SetEventConfig("DisplayConfigMITKBase.xml");
interactionEventHandler->AddEventConfig("DisplayConfigCrosshair.xml");
break;
}
case MITKRotationUncoupled:
{
interactionEventHandler->SetEventConfig("DisplayConfigMITKBase.xml");
interactionEventHandler->AddEventConfig("DisplayConfigRotation.xml");
break;
}
case MITKRotationCoupled:
{
interactionEventHandler->SetEventConfig("DisplayConfigMITKBase.xml");
interactionEventHandler->AddEventConfig("DisplayConfigRotation.xml");
interactionEventHandler->AddEventConfig("DisplayConfigActivateCoupling.xml");
break;
}
case MITKSwivel:
{
interactionEventHandler->SetEventConfig("DisplayConfigMITKBase.xml");
interactionEventHandler->AddEventConfig("DisplayConfigSwivel.xml");
break;
}
// PACS MODE
case PACSBase:
{
interactionEventHandler->SetEventConfig("DisplayConfigPACSBase.xml");
break;
}
case PACSStandard:
{
interactionEventHandler->SetEventConfig("DisplayConfigPACSBase.xml");
interactionEventHandler->AddEventConfig("DisplayConfigCrosshair.xml");
break;
}
case PACSLevelWindow:
{
interactionEventHandler->SetEventConfig("DisplayConfigPACSBase.xml");
interactionEventHandler->AddEventConfig("DisplayConfigPACSLevelWindow.xml");
break;
}
case PACSPan:
{
interactionEventHandler->SetEventConfig("DisplayConfigPACSBase.xml");
interactionEventHandler->AddEventConfig("DisplayConfigPACSPan.xml");
break;
}
case PACSScroll:
{
interactionEventHandler->SetEventConfig("DisplayConfigPACSBase.xml");
interactionEventHandler->AddEventConfig("DisplayConfigPACSScroll.xml");
break;
}
case PACSZoom:
{
interactionEventHandler->SetEventConfig("DisplayConfigPACSBase.xml");
interactionEventHandler->AddEventConfig("DisplayConfigPACSZoom.xml");
break;
}
default:
{
interactionEventHandler->SetEventConfig("DisplayConfigMITKBase.xml");
interactionEventHandler->AddEventConfig("DisplayConfigCrosshair.xml");
}
}
InvokeEvent(InteractionSchemeChangedEvent());
}
diff --git a/Modules/Core/src/Rendering/mitkBaseRenderer.cpp b/Modules/Core/src/Rendering/mitkBaseRenderer.cpp
index 58f2831295..881e066d0b 100644
--- a/Modules/Core/src/Rendering/mitkBaseRenderer.cpp
+++ b/Modules/Core/src/Rendering/mitkBaseRenderer.cpp
@@ -1,784 +1,789 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkBaseRenderer.h"
#include "mitkMapper.h"
#include "mitkResliceMethodProperty.h"
// Geometries
#include "mitkPlaneGeometry.h"
#include "mitkSlicedGeometry3D.h"
// Controllers
#include "mitkCameraController.h"
#include "mitkCameraRotationController.h"
#include "mitkSliceNavigationController.h"
#include "mitkVtkLayerController.h"
#include "mitkInteractionConst.h"
#include "mitkProperties.h"
#include "mitkWeakPointerProperty.h"
// VTK
#include <vtkCamera.h>
#include <vtkLinearTransform.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkActor.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
+namespace mitk
+{
+ itkEventMacroDefinition(RendererResetEvent, itk::AnyEvent);
+}
+
mitk::BaseRenderer::BaseRendererMapType mitk::BaseRenderer::baseRendererMap;
mitk::BaseRenderer *mitk::BaseRenderer::GetInstance(vtkRenderWindow *renWin)
{
for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit)
{
if ((*mapit).first == renWin)
return (*mapit).second;
}
return nullptr;
}
void mitk::BaseRenderer::AddInstance(vtkRenderWindow *renWin, BaseRenderer *baseRenderer)
{
if (renWin == nullptr || baseRenderer == nullptr)
return;
// ensure that no BaseRenderer is managed twice
mitk::BaseRenderer::RemoveInstance(renWin);
baseRendererMap.insert(BaseRendererMapType::value_type(renWin, baseRenderer));
}
void mitk::BaseRenderer::RemoveInstance(vtkRenderWindow *renWin)
{
auto mapit = baseRendererMap.find(renWin);
if (mapit != baseRendererMap.end())
baseRendererMap.erase(mapit);
}
mitk::BaseRenderer *mitk::BaseRenderer::GetByName(const std::string &name)
{
for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit)
{
if ((*mapit).second->m_Name == name)
return (*mapit).second;
}
return nullptr;
}
vtkRenderWindow *mitk::BaseRenderer::GetRenderWindowByName(const std::string &name)
{
for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit)
{
if ((*mapit).second->m_Name == name)
return (*mapit).first;
}
return nullptr;
}
mitk::BaseRenderer::BaseRenderer(const char *name,
vtkRenderWindow *renWin)
: m_RenderWindow(nullptr),
m_VtkRenderer(nullptr),
m_MapperID(defaultMapper),
m_DataStorage(nullptr),
m_LastUpdateTime(0),
m_CameraController(nullptr),
m_SliceNavigationController(nullptr),
m_CameraRotationController(nullptr),
m_WorldTimeGeometry(nullptr),
m_CurrentWorldGeometry(nullptr),
m_CurrentWorldPlaneGeometry(nullptr),
m_Slice(0),
m_TimeStep(),
m_CurrentWorldPlaneGeometryUpdateTime(),
m_TimeStepUpdateTime(),
m_KeepDisplayedRegion(true),
m_CurrentWorldPlaneGeometryData(nullptr),
m_CurrentWorldPlaneGeometryNode(nullptr),
m_CurrentWorldPlaneGeometryTransformTime(0),
m_Name(name),
m_EmptyWorldGeometry(true),
m_NumberOfVisibleLODEnabledMappers(0)
{
m_Bounds[0] = 0;
m_Bounds[1] = 0;
m_Bounds[2] = 0;
m_Bounds[3] = 0;
m_Bounds[4] = 0;
m_Bounds[5] = 0;
if (name != nullptr)
{
m_Name = name;
}
else
{
m_Name = "unnamed renderer";
itkWarningMacro(<< "Created unnamed renderer. Bad for serialization. Please choose a name.");
}
if (renWin != nullptr)
{
m_RenderWindow = renWin;
m_RenderWindow->Register(nullptr);
}
else
{
itkWarningMacro(<< "Created mitkBaseRenderer without vtkRenderWindow present.");
}
// instances.insert( this );
// adding this BaseRenderer to the List of all BaseRenderer
m_BindDispatcherInteractor = new mitk::BindDispatcherInteractor(GetName());
WeakPointerProperty::Pointer rendererProp = WeakPointerProperty::New((itk::Object *)this);
m_CurrentWorldPlaneGeometry = mitk::PlaneGeometry::New();
m_CurrentWorldPlaneGeometryData = mitk::PlaneGeometryData::New();
m_CurrentWorldPlaneGeometryData->SetPlaneGeometry(m_CurrentWorldPlaneGeometry);
m_CurrentWorldPlaneGeometryNode = mitk::DataNode::New();
m_CurrentWorldPlaneGeometryNode->SetData(m_CurrentWorldPlaneGeometryData);
m_CurrentWorldPlaneGeometryNode->GetPropertyList()->SetProperty("renderer", rendererProp);
m_CurrentWorldPlaneGeometryNode->GetPropertyList()->SetProperty("layer", IntProperty::New(1000));
m_CurrentWorldPlaneGeometryNode->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New());
m_CurrentWorldPlaneGeometryNode->SetProperty("reslice.thickslices.num", mitk::IntProperty::New(1));
m_CurrentWorldPlaneGeometryTransformTime = m_CurrentWorldPlaneGeometryNode->GetVtkTransform()->GetMTime();
mitk::SliceNavigationController::Pointer sliceNavigationController = mitk::SliceNavigationController::New();
sliceNavigationController->SetRenderer(this);
sliceNavigationController->ConnectGeometrySliceEvent(this);
sliceNavigationController->ConnectGeometryUpdateEvent(this);
sliceNavigationController->ConnectGeometryTimeEvent(this, false);
m_SliceNavigationController = sliceNavigationController;
m_CameraRotationController = mitk::CameraRotationController::New();
m_CameraRotationController->SetRenderWindow(m_RenderWindow);
m_CameraRotationController->AcquireCamera();
m_CameraController = mitk::CameraController::New();
m_CameraController->SetRenderer(this);
m_VtkRenderer = vtkRenderer::New();
m_VtkRenderer->SetMaximumNumberOfPeels(16);
if (AntiAliasing::FastApproximate == RenderingManager::GetInstance()->GetAntiAliasing())
m_VtkRenderer->UseFXAAOn();
if (nullptr == mitk::VtkLayerController::GetInstance(m_RenderWindow))
mitk::VtkLayerController::AddInstance(m_RenderWindow, m_VtkRenderer);
mitk::VtkLayerController::GetInstance(m_RenderWindow)->InsertSceneRenderer(m_VtkRenderer);
}
mitk::BaseRenderer::~BaseRenderer()
{
if (m_VtkRenderer != nullptr)
{
m_VtkRenderer->Delete();
m_VtkRenderer = nullptr;
}
if (m_CameraController.IsNotNull())
m_CameraController->SetRenderer(nullptr);
mitk::VtkLayerController::RemoveInstance(m_RenderWindow);
RemoveAllLocalStorages();
m_DataStorage = nullptr;
if (m_BindDispatcherInteractor != nullptr)
{
delete m_BindDispatcherInteractor;
}
if (m_RenderWindow != nullptr)
{
m_RenderWindow->Delete();
m_RenderWindow = nullptr;
}
}
void mitk::BaseRenderer::SetMapperID(MapperSlotId id)
{
if (m_MapperID != id)
{
bool useDepthPeeling = Standard3D == id;
m_VtkRenderer->SetUseDepthPeeling(useDepthPeeling);
m_VtkRenderer->SetUseDepthPeelingForVolumes(useDepthPeeling);
m_MapperID = id;
this->Modified();
}
}
void mitk::BaseRenderer::RemoveAllLocalStorages()
{
- this->InvokeEvent(mitk::BaseRenderer::RendererResetEvent());
+ this->InvokeEvent(RendererResetEvent());
std::list<mitk::BaseLocalStorageHandler *>::iterator it;
for (it = m_RegisteredLocalStorageHandlers.begin(); it != m_RegisteredLocalStorageHandlers.end(); ++it)
(*it)->ClearLocalStorage(this, false);
m_RegisteredLocalStorageHandlers.clear();
}
void mitk::BaseRenderer::RegisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh)
{
m_RegisteredLocalStorageHandlers.push_back(lsh);
}
mitk::Dispatcher::Pointer mitk::BaseRenderer::GetDispatcher() const
{
return m_BindDispatcherInteractor->GetDispatcher();
}
void mitk::BaseRenderer::UnregisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh)
{
m_RegisteredLocalStorageHandlers.remove(lsh);
}
void mitk::BaseRenderer::SetDataStorage(DataStorage *storage)
{
if (storage != m_DataStorage && storage != nullptr)
{
m_DataStorage = storage;
m_BindDispatcherInteractor->SetDataStorage(m_DataStorage);
this->Modified();
}
}
const mitk::BaseRenderer::MapperSlotId mitk::BaseRenderer::defaultMapper = 1;
void mitk::BaseRenderer::Paint()
{
}
void mitk::BaseRenderer::Initialize()
{
}
void mitk::BaseRenderer::Resize(int w, int h)
{
this->m_RenderWindow->SetSize(w, h);
}
void mitk::BaseRenderer::InitRenderer(vtkRenderWindow *renderwindow)
{
if (m_RenderWindow != renderwindow)
{
if (m_RenderWindow != nullptr)
{
m_RenderWindow->Delete();
}
m_RenderWindow = renderwindow;
if (m_RenderWindow != nullptr)
{
m_RenderWindow->Register(nullptr);
}
}
RemoveAllLocalStorages();
if (m_CameraController.IsNotNull())
{
m_CameraController->SetRenderer(this);
}
}
void mitk::BaseRenderer::InitSize(int w, int h)
{
this->m_RenderWindow->SetSize(w, h);
}
void mitk::BaseRenderer::SetSlice(unsigned int slice)
{
if (m_Slice != slice)
{
m_Slice = slice;
if (m_WorldTimeGeometry.IsNotNull())
{
// get world geometry which may be rotated, for the current time step
SlicedGeometry3D *slicedWorldGeometry =
dynamic_cast<SlicedGeometry3D *>(m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep).GetPointer());
if (slicedWorldGeometry != nullptr)
{
// if slice position is part of the world geometry...
if (m_Slice >= slicedWorldGeometry->GetSlices())
// set the current worldplanegeomety as the selected 2D slice of the world geometry
m_Slice = slicedWorldGeometry->GetSlices() - 1;
SetCurrentWorldPlaneGeometry(slicedWorldGeometry->GetPlaneGeometry(m_Slice));
SetCurrentWorldGeometry(slicedWorldGeometry);
}
}
else
Modified();
}
}
void mitk::BaseRenderer::SetTimeStep(unsigned int timeStep)
{
if (m_TimeStep != timeStep)
{
m_TimeStep = timeStep;
m_TimeStepUpdateTime.Modified();
if (m_WorldTimeGeometry.IsNotNull())
{
if (m_TimeStep >= m_WorldTimeGeometry->CountTimeSteps())
m_TimeStep = m_WorldTimeGeometry->CountTimeSteps() - 1;
SlicedGeometry3D *slicedWorldGeometry =
dynamic_cast<SlicedGeometry3D *>(m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep).GetPointer());
if (slicedWorldGeometry != nullptr)
{
SetCurrentWorldPlaneGeometry(slicedWorldGeometry->GetPlaneGeometry(m_Slice));
SetCurrentWorldGeometry(slicedWorldGeometry);
}
}
else
Modified();
}
}
mitk::TimeStepType mitk::BaseRenderer::GetTimeStep(const mitk::BaseData *data) const
{
if ((data == nullptr) || (data->IsInitialized() == false))
{
return -1;
}
return data->GetTimeGeometry()->TimePointToTimeStep(GetTime());
}
mitk::ScalarType mitk::BaseRenderer::GetTime() const
{
if (m_WorldTimeGeometry.IsNull())
{
return 0;
}
else
{
ScalarType timeInMS = m_WorldTimeGeometry->TimeStepToTimePoint(GetTimeStep());
if (timeInMS == itk::NumericTraits<mitk::ScalarType>::NonpositiveMin())
return 0;
else
return timeInMS;
}
}
void mitk::BaseRenderer::SetWorldTimeGeometry(const mitk::TimeGeometry *geometry)
{
assert(geometry != nullptr);
itkDebugMacro("setting WorldTimeGeometry to " << geometry);
if (m_WorldTimeGeometry != geometry)
{
if (geometry->GetBoundingBoxInWorld()->GetDiagonalLength2() == 0)
return;
m_WorldTimeGeometry = geometry;
itkDebugMacro("setting WorldTimeGeometry to " << m_WorldTimeGeometry);
if (m_TimeStep >= m_WorldTimeGeometry->CountTimeSteps())
m_TimeStep = m_WorldTimeGeometry->CountTimeSteps() - 1;
BaseGeometry *geometry3d;
geometry3d = m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep);
SetWorldGeometry3D(geometry3d);
}
}
void mitk::BaseRenderer::SetWorldGeometry3D(const mitk::BaseGeometry *geometry)
{
itkDebugMacro("setting WorldGeometry3D to " << geometry);
if (geometry->GetBoundingBox()->GetDiagonalLength2() == 0)
return;
const SlicedGeometry3D *slicedWorldGeometry;
slicedWorldGeometry = dynamic_cast<const SlicedGeometry3D *>(geometry);
PlaneGeometry::ConstPointer geometry2d;
if (slicedWorldGeometry != nullptr)
{
if (m_Slice >= slicedWorldGeometry->GetSlices() && (m_Slice != 0))
m_Slice = slicedWorldGeometry->GetSlices() - 1;
geometry2d = slicedWorldGeometry->GetPlaneGeometry(m_Slice);
if (geometry2d.IsNull())
{
PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New();
plane->InitializeStandardPlane(slicedWorldGeometry);
geometry2d = plane;
}
SetCurrentWorldGeometry(slicedWorldGeometry);
}
else
{
geometry2d = dynamic_cast<const PlaneGeometry *>(geometry);
if (geometry2d.IsNull())
{
PlaneGeometry::Pointer plane = PlaneGeometry::New();
plane->InitializeStandardPlane(geometry);
geometry2d = plane;
}
SetCurrentWorldGeometry(geometry);
}
SetCurrentWorldPlaneGeometry(geometry2d); // calls Modified()
if (m_CurrentWorldPlaneGeometry.IsNull())
itkWarningMacro("m_CurrentWorldPlaneGeometry is nullptr");
}
void mitk::BaseRenderer::SetCurrentWorldPlaneGeometry(const mitk::PlaneGeometry *geometry2d)
{
if (m_CurrentWorldPlaneGeometry != geometry2d)
{
m_CurrentWorldPlaneGeometry = geometry2d->Clone();
m_CurrentWorldPlaneGeometryData->SetPlaneGeometry(m_CurrentWorldPlaneGeometry);
m_CurrentWorldPlaneGeometryUpdateTime.Modified();
Modified();
}
}
void mitk::BaseRenderer::SendUpdateSlice()
{
m_CurrentWorldPlaneGeometryUpdateTime.Modified();
}
int *mitk::BaseRenderer::GetSize() const
{
return this->m_RenderWindow->GetSize();
}
int *mitk::BaseRenderer::GetViewportSize() const
{
return this->m_VtkRenderer->GetSize();
}
void mitk::BaseRenderer::SetCurrentWorldGeometry(const mitk::BaseGeometry *geometry)
{
m_CurrentWorldGeometry = geometry;
if (geometry == nullptr)
{
m_Bounds[0] = 0;
m_Bounds[1] = 0;
m_Bounds[2] = 0;
m_Bounds[3] = 0;
m_Bounds[4] = 0;
m_Bounds[5] = 0;
m_EmptyWorldGeometry = true;
return;
}
BoundingBox::Pointer boundingBox = m_CurrentWorldGeometry->CalculateBoundingBoxRelativeToTransform(nullptr);
const BoundingBox::BoundsArrayType &worldBounds = boundingBox->GetBounds();
m_Bounds[0] = worldBounds[0];
m_Bounds[1] = worldBounds[1];
m_Bounds[2] = worldBounds[2];
m_Bounds[3] = worldBounds[3];
m_Bounds[4] = worldBounds[4];
m_Bounds[5] = worldBounds[5];
if (boundingBox->GetDiagonalLength2() <= mitk::eps)
m_EmptyWorldGeometry = true;
else
m_EmptyWorldGeometry = false;
}
void mitk::BaseRenderer::SetGeometry(const itk::EventObject &geometrySendEvent)
{
const auto *sendEvent =
dynamic_cast<const SliceNavigationController::GeometrySendEvent *>(&geometrySendEvent);
assert(sendEvent != nullptr);
SetWorldTimeGeometry(sendEvent->GetTimeGeometry());
}
void mitk::BaseRenderer::UpdateGeometry(const itk::EventObject &geometryUpdateEvent)
{
const auto *updateEvent =
dynamic_cast<const SliceNavigationController::GeometryUpdateEvent *>(&geometryUpdateEvent);
if (updateEvent == nullptr)
return;
if (m_CurrentWorldGeometry.IsNotNull())
{
auto *slicedWorldGeometry = dynamic_cast<const SlicedGeometry3D *>(m_CurrentWorldGeometry.GetPointer());
if (slicedWorldGeometry)
{
PlaneGeometry *geometry2D = slicedWorldGeometry->GetPlaneGeometry(m_Slice);
SetCurrentWorldPlaneGeometry(geometry2D); // calls Modified()
}
}
}
void mitk::BaseRenderer::SetGeometrySlice(const itk::EventObject &geometrySliceEvent)
{
const auto *sliceEvent =
dynamic_cast<const SliceNavigationController::GeometrySliceEvent *>(&geometrySliceEvent);
assert(sliceEvent != nullptr);
SetSlice(sliceEvent->GetPos());
}
void mitk::BaseRenderer::SetGeometryTime(const itk::EventObject &geometryTimeEvent)
{
const auto *timeEvent =
dynamic_cast<const SliceNavigationController::GeometryTimeEvent *>(&geometryTimeEvent);
assert(timeEvent != nullptr);
SetTimeStep(timeEvent->GetPos());
}
const double *mitk::BaseRenderer::GetBounds() const
{
return m_Bounds;
}
void mitk::BaseRenderer::DrawOverlayMouse(mitk::Point2D &itkNotUsed(p2d))
{
MITK_INFO << "BaseRenderer::DrawOverlayMouse()- should be inconcret implementation OpenGLRenderer." << std::endl;
}
void mitk::BaseRenderer::RequestUpdate()
{
SetConstrainZoomingAndPanning(true);
RenderingManager::GetInstance()->RequestUpdate(this->m_RenderWindow);
}
void mitk::BaseRenderer::ForceImmediateUpdate()
{
RenderingManager::GetInstance()->ForceImmediateUpdate(this->m_RenderWindow);
}
unsigned int mitk::BaseRenderer::GetNumberOfVisibleLODEnabledMappers() const
{
return m_NumberOfVisibleLODEnabledMappers;
}
/*!
Sets the new Navigation controller
*/
void mitk::BaseRenderer::SetSliceNavigationController(mitk::SliceNavigationController *SlicenavigationController)
{
if (SlicenavigationController == nullptr)
return;
// copy worldgeometry
SlicenavigationController->SetInputWorldTimeGeometry(SlicenavigationController->GetCreatedWorldGeometry());
SlicenavigationController->Update();
// set new
m_SliceNavigationController = SlicenavigationController;
m_SliceNavigationController->SetRenderer(this);
if (m_SliceNavigationController.IsNotNull())
{
m_SliceNavigationController->ConnectGeometrySliceEvent(this);
m_SliceNavigationController->ConnectGeometryUpdateEvent(this);
m_SliceNavigationController->ConnectGeometryTimeEvent(this, false);
}
}
void mitk::BaseRenderer::DisplayToWorld(const Point2D &displayPoint, Point3D &worldIndex) const
{
if (m_MapperID == BaseRenderer::Standard2D)
{
double display[3], *world;
// For the rigth z-position in display coordinates, take the focal point, convert it to display and use it for
// correct depth.
double *displayCoord;
double cameraFP[4];
// Get camera focal point and position. Convert to display (screen)
// coordinates. We need a depth value for z-buffer.
this->GetVtkRenderer()->GetActiveCamera()->GetFocalPoint(cameraFP);
cameraFP[3] = 0.0;
this->GetVtkRenderer()->SetWorldPoint(cameraFP[0], cameraFP[1], cameraFP[2], cameraFP[3]);
this->GetVtkRenderer()->WorldToDisplay();
displayCoord = this->GetVtkRenderer()->GetDisplayPoint();
// now convert the display point to world coordinates
display[0] = displayPoint[0];
display[1] = displayPoint[1];
display[2] = displayCoord[2];
this->GetVtkRenderer()->SetDisplayPoint(display);
this->GetVtkRenderer()->DisplayToWorld();
world = this->GetVtkRenderer()->GetWorldPoint();
for (int i = 0; i < 3; i++)
{
worldIndex[i] = world[i] / world[3];
}
}
else if (m_MapperID == BaseRenderer::Standard3D)
{
PickWorldPoint(
displayPoint,
worldIndex); // Seems to be the same code as above, but subclasses may contain different implementations.
}
return;
}
void mitk::BaseRenderer::DisplayToPlane(const Point2D &displayPoint, Point2D &planePointInMM) const
{
if (m_MapperID == BaseRenderer::Standard2D)
{
Point3D worldPoint;
this->DisplayToWorld(displayPoint, worldPoint);
this->m_CurrentWorldPlaneGeometry->Map(worldPoint, planePointInMM);
}
else if (m_MapperID == BaseRenderer::Standard3D)
{
MITK_WARN << "No conversion possible with 3D mapper.";
return;
}
return;
}
void mitk::BaseRenderer::WorldToDisplay(const Point3D &worldIndex, Point2D &displayPoint) const
{
double world[4], *display;
world[0] = worldIndex[0];
world[1] = worldIndex[1];
world[2] = worldIndex[2];
world[3] = 1.0;
this->GetVtkRenderer()->SetWorldPoint(world);
this->GetVtkRenderer()->WorldToDisplay();
display = this->GetVtkRenderer()->GetDisplayPoint();
displayPoint[0] = display[0];
displayPoint[1] = display[1];
return;
}
void mitk::BaseRenderer::WorldToView(const mitk::Point3D &worldIndex, mitk::Point2D &viewPoint) const
{
double world[4], *view;
world[0] = worldIndex[0];
world[1] = worldIndex[1];
world[2] = worldIndex[2];
world[3] = 1.0;
this->GetVtkRenderer()->SetWorldPoint(world);
this->GetVtkRenderer()->WorldToView();
view = this->GetVtkRenderer()->GetViewPoint();
this->GetVtkRenderer()->ViewToNormalizedViewport(view[0], view[1], view[2]);
viewPoint[0] = view[0] * this->GetViewportSize()[0];
viewPoint[1] = view[1] * this->GetViewportSize()[1];
return;
}
void mitk::BaseRenderer::PlaneToDisplay(const Point2D &planePointInMM, Point2D &displayPoint) const
{
Point3D worldPoint;
this->m_CurrentWorldPlaneGeometry->Map(planePointInMM, worldPoint);
this->WorldToDisplay(worldPoint, displayPoint);
return;
}
void mitk::BaseRenderer::PlaneToView(const Point2D &planePointInMM, Point2D &viewPoint) const
{
Point3D worldPoint;
this->m_CurrentWorldPlaneGeometry->Map(planePointInMM, worldPoint);
this->WorldToView(worldPoint,viewPoint);
return;
}
double mitk::BaseRenderer::GetScaleFactorMMPerDisplayUnit() const
{
if (this->GetMapperID() == BaseRenderer::Standard2D)
{
// GetParallelScale returns half of the height of the render window in mm.
// Divided by the half size of the Display size in pixel givest the mm per pixel.
return this->GetVtkRenderer()->GetActiveCamera()->GetParallelScale() * 2.0 / GetViewportSize()[1];
}
else
return 1.0;
}
mitk::Point2D mitk::BaseRenderer::GetDisplaySizeInMM() const
{
Point2D dispSizeInMM;
dispSizeInMM[0] = GetSizeX() * GetScaleFactorMMPerDisplayUnit();
dispSizeInMM[1] = GetSizeY() * GetScaleFactorMMPerDisplayUnit();
return dispSizeInMM;
}
mitk::Point2D mitk::BaseRenderer::GetViewportSizeInMM() const
{
Point2D dispSizeInMM;
dispSizeInMM[0] = GetViewportSize()[0] * GetScaleFactorMMPerDisplayUnit();
dispSizeInMM[1] = GetViewportSize()[1] * GetScaleFactorMMPerDisplayUnit();
return dispSizeInMM;
}
mitk::Point2D mitk::BaseRenderer::GetOriginInMM() const
{
Point2D originPx;
originPx[0] = m_VtkRenderer->GetOrigin()[0];
originPx[1] = m_VtkRenderer->GetOrigin()[1];
Point2D displayGeometryOriginInMM;
DisplayToPlane(originPx, displayGeometryOriginInMM); // top left of the render window (Origin)
return displayGeometryOriginInMM;
}
void mitk::BaseRenderer::SetConstrainZoomingAndPanning(bool constrain)
{
m_ConstrainZoomingAndPanning = constrain;
if (m_ConstrainZoomingAndPanning)
{
this->GetCameraController()->AdjustCameraToPlane();
}
}
mitk::Point3D mitk::BaseRenderer::Map2DRendererPositionTo3DWorldPosition(const Point2D &mousePosition) const
{
// DEPRECATED: Map2DRendererPositionTo3DWorldPosition is deprecated. use DisplayToWorldInstead
Point3D position;
DisplayToWorld(mousePosition, position);
return position;
}
void mitk::BaseRenderer::PrintSelf(std::ostream &os, itk::Indent indent) const
{
os << indent << " MapperID: " << m_MapperID << std::endl;
os << indent << " Slice: " << m_Slice << std::endl;
os << indent << " TimeStep: " << m_TimeStep << std::endl;
os << indent << " CurrentWorldPlaneGeometry: ";
if (m_CurrentWorldPlaneGeometry.IsNull())
os << "nullptr" << std::endl;
else
m_CurrentWorldPlaneGeometry->Print(os, indent);
os << indent << " CurrentWorldPlaneGeometryUpdateTime: " << m_CurrentWorldPlaneGeometryUpdateTime << std::endl;
os << indent << " CurrentWorldPlaneGeometryTransformTime: " << m_CurrentWorldPlaneGeometryTransformTime << std::endl;
Superclass::PrintSelf(os, indent);
}
diff --git a/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp b/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp
index 5e4e797c38..12555a0d66 100644
--- a/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp
+++ b/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp
@@ -1,1153 +1,1153 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// MITK
#include <mitkAbstractTransformGeometry.h>
#include <mitkDataNode.h>
#include <mitkImageSliceSelector.h>
#include <mitkLevelWindowProperty.h>
#include <mitkLookupTableProperty.h>
#include <mitkPixelType.h>
#include <mitkPlaneGeometry.h>
#include <mitkProperties.h>
#include <mitkPropertyNameHelper.h>
#include <mitkResliceMethodProperty.h>
#include <mitkVtkResliceInterpolationProperty.h>
//#include <mitkTransferFunction.h>
#include "mitkImageStatisticsHolder.h"
#include "mitkPlaneClipping.h"
#include <mitkTransferFunctionProperty.h>
// MITK Rendering
#include "mitkImageVtkMapper2D.h"
#include "vtkMitkLevelWindowFilter.h"
#include "vtkMitkThickSlicesFilter.h"
#include "vtkNeverTranslucentTexture.h"
// VTK
#include <vtkCamera.h>
#include <vtkCellArray.h>
#include <vtkColorTransferFunction.h>
#include <vtkGeneralTransform.h>
#include <vtkImageChangeInformation.h>
#include <vtkImageData.h>
#include <vtkImageExtractComponents.h>
#include <vtkImageReslice.h>
#include <vtkLookupTable.h>
#include <vtkMatrix4x4.h>
#include <vtkPlaneSource.h>
#include <vtkPoints.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkTransform.h>
// ITK
#include <itkRGBAPixel.h>
#include <mitkRenderingModeProperty.h>
namespace
{
bool IsBinaryImage(mitk::Image* image)
{
if (nullptr != image && image->IsInitialized())
{
bool isBinary = true;
auto statistics = image->GetStatistics();
const auto numTimeSteps = image->GetTimeSteps();
for (std::remove_const_t<decltype(numTimeSteps)> t = 0; t < numTimeSteps; ++t)
{
const auto numChannels = image->GetNumberOfChannels();
for (std::remove_const_t<decltype(numChannels)> c = 0; c < numChannels; ++c)
{
auto minValue = statistics->GetScalarValueMin(t, c);
auto maxValue = statistics->GetScalarValueMax(t, c);
if (std::abs(maxValue - minValue) < mitk::eps)
continue;
auto min2ndValue = statistics->GetScalarValue2ndMin(t, c);
auto max2ndValue = statistics->GetScalarValue2ndMax(t, c);
if (std::abs(maxValue - min2ndValue) < mitk::eps && std::abs(max2ndValue - minValue) < mitk::eps)
continue;
isBinary = false;
break;
}
if (!isBinary)
break;
}
return isBinary;
}
return false;
}
}
mitk::ImageVtkMapper2D::ImageVtkMapper2D()
{
}
mitk::ImageVtkMapper2D::~ImageVtkMapper2D()
{
// The 3D RW Mapper (PlaneGeometryDataVtkMapper3D) is listening to this event,
// in order to delete the images from the 3D RW.
this->InvokeEvent(itk::DeleteEvent());
}
// set the two points defining the textured plane according to the dimension and spacing
void mitk::ImageVtkMapper2D::GeneratePlane(mitk::BaseRenderer *renderer, double planeBounds[6])
{
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
float depth = this->CalculateLayerDepth(renderer);
// Set the origin to (xMin; yMin; depth) of the plane. This is necessary for obtaining the correct
// plane size in crosshair rotation and swivel mode.
localStorage->m_Plane->SetOrigin(planeBounds[0], planeBounds[2], depth);
// These two points define the axes of the plane in combination with the origin.
// Point 1 is the x-axis and point 2 the y-axis.
// Each plane is transformed according to the view (axial, coronal and saggital) afterwards.
localStorage->m_Plane->SetPoint1(planeBounds[1], planeBounds[2], depth); // P1: (xMax, yMin, depth)
localStorage->m_Plane->SetPoint2(planeBounds[0], planeBounds[3], depth); // P2: (xMin, yMax, depth)
}
float mitk::ImageVtkMapper2D::CalculateLayerDepth(mitk::BaseRenderer *renderer)
{
// get the clipping range to check how deep into z direction we can render images
double maxRange = renderer->GetVtkRenderer()->GetActiveCamera()->GetClippingRange()[1];
// Due to a VTK bug, we cannot use the whole clipping range. /100 is empirically determined
float depth = -maxRange * 0.01; // divide by 100
int layer = 0;
GetDataNode()->GetIntProperty("layer", layer, renderer);
// add the layer property for each image to render images with a higher layer on top of the others
depth += layer * 10; //*10: keep some room for each image (e.g. for ODFs in between)
if (depth > 0.0f)
{
depth = 0.0f;
MITK_WARN << "Layer value exceeds clipping range. Set to minimum instead.";
}
return depth;
}
const mitk::Image *mitk::ImageVtkMapper2D::GetInput(void)
{
return static_cast<const mitk::Image *>(GetDataNode()->GetData());
}
vtkProp *mitk::ImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer)
{
// return the actor corresponding to the renderer
return m_LSH.GetLocalStorage(renderer)->m_PublicActors;
}
void mitk::ImageVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
auto *image = const_cast<mitk::Image *>(this->GetInput());
mitk::DataNode *datanode = this->GetDataNode();
if (nullptr == image || !image->IsInitialized())
{
this->SetToInvalidState(localStorage);
return;
}
// check if there is a valid worldGeometry
const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry();
if (nullptr == worldGeometry || !worldGeometry->IsValid() || !worldGeometry->HasReferenceGeometry())
{
this->SetToInvalidState(localStorage);
return;
}
image->Update();
localStorage->m_PublicActors = localStorage->m_Actors.Get();
// early out if there is no intersection of the current rendering geometry
// and the geometry of the image that is to be rendered.
if (!RenderingGeometryIntersectsImage(worldGeometry, image->GetSlicedGeometry()))
{
this->SetToInvalidState(localStorage);
return;
}
// set main input for ExtractSliceFilter
localStorage->m_Reslicer->SetInput(image);
localStorage->m_Reslicer->SetWorldGeometry(worldGeometry);
localStorage->m_Reslicer->SetTimeStep(this->GetTimestep());
// set the transformation of the image to adapt reslice axis
localStorage->m_Reslicer->SetResliceTransformByGeometry(
image->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep()));
// is the geometry of the slice based on the input image or the worldgeometry?
bool inPlaneResampleExtentByGeometry = false;
datanode->GetBoolProperty("in plane resample extent by geometry", inPlaneResampleExtentByGeometry, renderer);
localStorage->m_Reslicer->SetInPlaneResampleExtentByGeometry(inPlaneResampleExtentByGeometry);
// Initialize the interpolation mode for resampling; switch to nearest
// neighbor if the input image is too small.
if ((image->GetDimension() >= 3) && (image->GetDimension(2) > 1))
{
VtkResliceInterpolationProperty *resliceInterpolationProperty;
datanode->GetProperty(resliceInterpolationProperty, "reslice interpolation", renderer);
int interpolationMode = VTK_RESLICE_NEAREST;
if (resliceInterpolationProperty != nullptr)
{
interpolationMode = resliceInterpolationProperty->GetInterpolation();
}
switch (interpolationMode)
{
case VTK_RESLICE_NEAREST:
localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST);
break;
case VTK_RESLICE_LINEAR:
localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_LINEAR);
break;
case VTK_RESLICE_CUBIC:
localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_CUBIC);
break;
}
}
else
{
localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST);
}
// set the vtk output property to true, makes sure that no unneeded mitk image convertion
// is done.
localStorage->m_Reslicer->SetVtkOutputRequest(true);
// Thickslicing
int thickSlicesMode = 0;
int thickSlicesNum = 1;
// Thick slices parameters
if (image->GetPixelType().GetNumberOfComponents() == 1) // for now only single component are allowed
{
DataNode *dn = renderer->GetCurrentWorldPlaneGeometryNode();
if (dn)
{
ResliceMethodProperty *resliceMethodEnumProperty = nullptr;
if (dn->GetProperty(resliceMethodEnumProperty, "reslice.thickslices", renderer) && resliceMethodEnumProperty)
thickSlicesMode = resliceMethodEnumProperty->GetValueAsId();
IntProperty *intProperty = nullptr;
if (dn->GetProperty(intProperty, "reslice.thickslices.num", renderer) && intProperty)
{
thickSlicesNum = intProperty->GetValue();
if (thickSlicesNum < 1)
thickSlicesNum = 1;
}
}
else
{
MITK_WARN << "no associated widget plane data tree node found";
}
}
const auto *planeGeometry = dynamic_cast<const PlaneGeometry *>(worldGeometry);
if (thickSlicesMode > 0)
{
double dataZSpacing = 1.0;
Vector3D normInIndex, normal;
const auto *abstractGeometry =
dynamic_cast<const AbstractTransformGeometry *>(worldGeometry);
if (abstractGeometry != nullptr)
normal = abstractGeometry->GetPlane()->GetNormal();
else
{
if (planeGeometry != nullptr)
{
normal = planeGeometry->GetNormal();
}
else
return; // no fitting geometry set
}
normal.Normalize();
image->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())->WorldToIndex(normal, normInIndex);
dataZSpacing = 1.0 / normInIndex.GetNorm();
localStorage->m_Reslicer->SetOutputDimensionality(3);
localStorage->m_Reslicer->SetOutputSpacingZDirection(dataZSpacing);
localStorage->m_Reslicer->SetOutputExtentZDirection(-thickSlicesNum, 0 + thickSlicesNum);
// Do the reslicing. Modified() is called to make sure that the reslicer is
// executed even though the input geometry information did not change; this
// is necessary when the input /em data, but not the /em geometry changes.
localStorage->m_TSFilter->SetThickSliceMode(thickSlicesMode - 1);
localStorage->m_TSFilter->SetInputData(localStorage->m_Reslicer->GetVtkOutput());
// vtkFilter=>mitkFilter=>vtkFilter update mechanism will fail without calling manually
localStorage->m_Reslicer->Modified();
localStorage->m_Reslicer->Update();
localStorage->m_TSFilter->Modified();
localStorage->m_TSFilter->Update();
localStorage->m_ReslicedImage = localStorage->m_TSFilter->GetOutput();
}
else
{
// this is needed when thick mode was enable bevore. These variable have to be reset to default values
localStorage->m_Reslicer->SetOutputDimensionality(2);
localStorage->m_Reslicer->SetOutputSpacingZDirection(1.0);
localStorage->m_Reslicer->SetOutputExtentZDirection(0, 0);
localStorage->m_Reslicer->Modified();
// start the pipeline with updating the largest possible, needed if the geometry of the input has changed
localStorage->m_Reslicer->UpdateLargestPossibleRegion();
localStorage->m_ReslicedImage = localStorage->m_Reslicer->GetVtkOutput();
}
// Bounds information for reslicing (only reuqired if reference geometry
// is present)
// this used for generating a vtkPLaneSource with the right size
double sliceBounds[6];
for (auto &sliceBound : sliceBounds)
{
sliceBound = 0.0;
}
localStorage->m_Reslicer->GetClippedPlaneBounds(sliceBounds);
// get the spacing of the slice
localStorage->m_mmPerPixel = localStorage->m_Reslicer->GetOutputSpacing();
// calculate minimum bounding rect of IMAGE in texture
{
double textureClippingBounds[6];
for (auto &textureClippingBound : textureClippingBounds)
{
textureClippingBound = 0.0;
}
// Calculate the actual bounds of the transformed plane clipped by the
// dataset bounding box; this is required for drawing the texture at the
// correct position during 3D mapping.
mitk::PlaneClipping::CalculateClippedPlaneBounds(image->GetGeometry(), planeGeometry, textureClippingBounds);
textureClippingBounds[0] = static_cast<int>(textureClippingBounds[0] / localStorage->m_mmPerPixel[0] + 0.5);
textureClippingBounds[1] = static_cast<int>(textureClippingBounds[1] / localStorage->m_mmPerPixel[0] + 0.5);
textureClippingBounds[2] = static_cast<int>(textureClippingBounds[2] / localStorage->m_mmPerPixel[1] + 0.5);
textureClippingBounds[3] = static_cast<int>(textureClippingBounds[3] / localStorage->m_mmPerPixel[1] + 0.5);
// clipping bounds for cutting the image
localStorage->m_LevelWindowFilter->SetClippingBounds(textureClippingBounds);
}
// get the number of scalar components to distinguish between different image types
int numberOfComponents = localStorage->m_ReslicedImage->GetNumberOfScalarComponents();
// get the binary property
bool binary = false;
bool binaryOutline = false;
datanode->GetBoolProperty("binary", binary, renderer);
if (binary) // binary image
{
datanode->GetBoolProperty("outline binary", binaryOutline, renderer);
if (binaryOutline) // contour rendering
{
// get pixel type of vtk image
- itk::ImageIOBase::IOComponentType componentType = static_cast<itk::ImageIOBase::IOComponentType>(image->GetPixelType().GetComponentType());
+ auto componentType = image->GetPixelType().GetComponentType();
switch (componentType)
{
- case itk::ImageIOBase::UCHAR:
+ case itk::IOComponentEnum::UCHAR:
// generate contours/outlines
localStorage->m_OutlinePolyData = CreateOutlinePolyData<unsigned char>(renderer);
break;
- case itk::ImageIOBase::USHORT:
+ case itk::IOComponentEnum::USHORT:
// generate contours/outlines
localStorage->m_OutlinePolyData = CreateOutlinePolyData<unsigned short>(renderer);
break;
default:
binaryOutline = false;
this->ApplyLookuptable(renderer);
MITK_WARN << "Type of all binary images should be unsigned char or unsigned short. Outline does not work on other pixel types!";
}
if (binaryOutline) // binary outline is still true --> add outline
{
float binaryOutlineWidth = 1.0;
if (datanode->GetFloatProperty("outline width", binaryOutlineWidth, renderer))
{
float binaryOutlineShadowWidth = 1.5;
datanode->GetFloatProperty("outline shadow width", binaryOutlineShadowWidth, renderer);
localStorage->m_ShadowOutlineActor->GetProperty()->SetLineWidth(binaryOutlineWidth * binaryOutlineShadowWidth);
localStorage->m_ImageActor->GetProperty()->SetLineWidth(binaryOutlineWidth);
}
}
}
else // standard binary image
{
if (numberOfComponents != 1)
{
MITK_ERROR << "Rendering Error: Binary Images with more then 1 component are not supported!";
}
}
}
this->ApplyOpacity(renderer);
this->ApplyRenderingMode(renderer);
// do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter)
localStorage->m_Texture->SetColorModeToDirectScalars();
int displayedComponent = 0;
if (datanode->GetIntProperty("Image.Displayed Component", displayedComponent, renderer) && numberOfComponents > 1)
{
localStorage->m_VectorComponentExtractor->SetComponents(displayedComponent);
localStorage->m_VectorComponentExtractor->SetInputData(localStorage->m_ReslicedImage);
localStorage->m_LevelWindowFilter->SetInputConnection(localStorage->m_VectorComponentExtractor->GetOutputPort(0));
}
else
{
// connect the input with the levelwindow filter
localStorage->m_LevelWindowFilter->SetInputData(localStorage->m_ReslicedImage);
}
// check for texture interpolation property
bool textureInterpolation = false;
GetDataNode()->GetBoolProperty("texture interpolation", textureInterpolation, renderer);
// set the interpolation modus according to the property
localStorage->m_Texture->SetInterpolate(textureInterpolation);
// connect the texture with the output of the levelwindow filter
localStorage->m_Texture->SetInputConnection(localStorage->m_LevelWindowFilter->GetOutputPort());
this->TransformActor(renderer);
if (binary && binaryOutline) // connect the mapper with the polyData which contains the lines
{
// We need the contour for the binary outline property as actor
localStorage->m_Mapper->SetInputData(localStorage->m_OutlinePolyData);
localStorage->m_ImageActor->SetTexture(nullptr); // no texture for contours
bool binaryOutlineShadow = false;
datanode->GetBoolProperty("outline binary shadow", binaryOutlineShadow, renderer);
if (binaryOutlineShadow)
{
localStorage->m_ShadowOutlineActor->SetVisibility(true);
}
else
{
localStorage->m_ShadowOutlineActor->SetVisibility(false);
}
}
else
{ // Connect the mapper with the input texture. This is the standard case.
// setup the textured plane
this->GeneratePlane(renderer, sliceBounds);
// set the plane as input for the mapper
localStorage->m_Mapper->SetInputConnection(localStorage->m_Plane->GetOutputPort());
// set the texture for the actor
localStorage->m_ImageActor->SetTexture(localStorage->m_Texture);
localStorage->m_ShadowOutlineActor->SetVisibility(false);
}
// We have been modified => save this for next Update()
localStorage->m_LastUpdateTime.Modified();
}
void mitk::ImageVtkMapper2D::ApplyLevelWindow(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = this->GetLocalStorage(renderer);
LevelWindow levelWindow;
this->GetDataNode()->GetLevelWindow(levelWindow, renderer, "levelwindow");
localStorage->m_LevelWindowFilter->GetLookupTable()->SetRange(levelWindow.GetLowerWindowBound(),
levelWindow.GetUpperWindowBound());
mitk::LevelWindow opacLevelWindow;
if (this->GetDataNode()->GetLevelWindow(opacLevelWindow, renderer, "opaclevelwindow"))
{
// pass the opaque level window to the filter
localStorage->m_LevelWindowFilter->SetMinOpacity(opacLevelWindow.GetLowerWindowBound());
localStorage->m_LevelWindowFilter->SetMaxOpacity(opacLevelWindow.GetUpperWindowBound());
}
else
{
// no opaque level window
localStorage->m_LevelWindowFilter->SetMinOpacity(0.0);
localStorage->m_LevelWindowFilter->SetMaxOpacity(255.0);
}
}
void mitk::ImageVtkMapper2D::ApplyColor(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = this->GetLocalStorage(renderer);
float rgb[3] = {1.0f, 1.0f, 1.0f};
// check for color prop and use it for rendering if it exists
// binary image hovering & binary image selection
bool hover = false;
bool selected = false;
bool binary = false;
GetDataNode()->GetBoolProperty("binaryimage.ishovering", hover, renderer);
GetDataNode()->GetBoolProperty("selected", selected, renderer);
GetDataNode()->GetBoolProperty("binary", binary, renderer);
if (binary && hover && !selected)
{
mitk::ColorProperty::Pointer colorprop =
dynamic_cast<mitk::ColorProperty *>(GetDataNode()->GetProperty("binaryimage.hoveringcolor", renderer));
if (colorprop.IsNotNull())
{
memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float));
}
else
{
GetDataNode()->GetColor(rgb, renderer, "color");
}
}
if (binary && selected)
{
mitk::ColorProperty::Pointer colorprop =
dynamic_cast<mitk::ColorProperty *>(GetDataNode()->GetProperty("binaryimage.selectedcolor", renderer));
if (colorprop.IsNotNull())
{
memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float));
}
else
{
GetDataNode()->GetColor(rgb, renderer, "color");
}
}
if (!binary || (!hover && !selected))
{
GetDataNode()->GetColor(rgb, renderer, "color");
}
double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; // conversion to double for VTK
localStorage->m_ShadowOutlineActor->GetProperty()->SetColor(rgbConv);
localStorage->m_ImageActor->GetProperty()->SetColor(rgbConv);
float shadowRGB[3] = {1.0f, 1.0f, 1.0f};
mitk::ColorProperty::Pointer colorprop =
dynamic_cast<mitk::ColorProperty *>(GetDataNode()->GetProperty("outline binary shadow color", renderer));
if (colorprop.IsNotNull())
{
memcpy(shadowRGB, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float));
}
double shadowRGBConv[3] = {(double)shadowRGB[0], (double)shadowRGB[1], (double)shadowRGB[2]}; // conversion to double for VTK
localStorage->m_ShadowOutlineActor->GetProperty()->SetColor(shadowRGBConv);
}
void mitk::ImageVtkMapper2D::ApplyOpacity(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = this->GetLocalStorage(renderer);
float opacity = 1.0f;
// check for opacity prop and use it for rendering if it exists
GetDataNode()->GetOpacity(opacity, renderer, "opacity");
// set the opacity according to the properties
localStorage->m_ImageActor->GetProperty()->SetOpacity(opacity);
localStorage->m_ShadowOutlineActor->GetProperty()->SetOpacity(opacity);
}
void mitk::ImageVtkMapper2D::ApplyRenderingMode(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
bool binary = false;
this->GetDataNode()->GetBoolProperty("binary", binary, renderer);
if (binary) // is it a binary image?
{
// for binary images, we always use our default LuT and map every value to (0,1)
// the opacity of 0 will always be 0.0. We never a apply a LuT/TfF nor a level window.
localStorage->m_LevelWindowFilter->SetLookupTable(localStorage->m_BinaryLookupTable);
}
else
{
// all other image types can make use of the rendering mode
int renderingMode = mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR;
mitk::RenderingModeProperty::Pointer mode =
dynamic_cast<mitk::RenderingModeProperty *>(this->GetDataNode()->GetProperty("Image Rendering.Mode", renderer));
if (mode.IsNotNull())
{
renderingMode = mode->GetRenderingMode();
}
switch (renderingMode)
{
case mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR:
MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_LookupTable_Color";
this->ApplyLookuptable(renderer);
this->ApplyLevelWindow(renderer);
break;
case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR:
MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_ColorTransferFunction_Color";
this->ApplyColorTransferFunction(renderer);
this->ApplyLevelWindow(renderer);
break;
case mitk::RenderingModeProperty::LOOKUPTABLE_COLOR:
MITK_DEBUG << "'Image Rendering.Mode' = LookupTable_Color";
this->ApplyLookuptable(renderer);
break;
case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR:
MITK_DEBUG << "'Image Rendering.Mode' = ColorTransferFunction_Color";
this->ApplyColorTransferFunction(renderer);
break;
default:
MITK_ERROR << "No valid 'Image Rendering.Mode' set. Using LOOKUPTABLE_LEVELWINDOW_COLOR instead.";
this->ApplyLookuptable(renderer);
this->ApplyLevelWindow(renderer);
break;
}
}
// we apply color for all images (including binaries).
this->ApplyColor(renderer);
}
void mitk::ImageVtkMapper2D::ApplyLookuptable(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
vtkLookupTable *usedLookupTable = localStorage->m_ColorLookupTable;
// If lookup table or transferfunction use is requested...
mitk::LookupTableProperty::Pointer lookupTableProp =
dynamic_cast<mitk::LookupTableProperty *>(this->GetDataNode()->GetProperty("LookupTable", renderer));
if (lookupTableProp.IsNotNull()) // is a lookuptable set?
{
usedLookupTable = lookupTableProp->GetLookupTable()->GetVtkLookupTable();
}
else
{
//"Image Rendering.Mode was set to use a lookup table but there is no property 'LookupTable'.
// A default (rainbow) lookup table will be used.
// Here have to do nothing. Warning for the user has been removed, due to unwanted console output
// in every interation of the rendering.
}
localStorage->m_LevelWindowFilter->SetLookupTable(usedLookupTable);
}
void mitk::ImageVtkMapper2D::ApplyColorTransferFunction(mitk::BaseRenderer *renderer)
{
mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast<mitk::TransferFunctionProperty *>(
this->GetDataNode()->GetProperty("Image Rendering.Transfer Function", renderer));
if (transferFunctionProp.IsNull())
{
MITK_ERROR << "'Image Rendering.Mode'' was set to use a color transfer function but there is no property 'Image "
"Rendering.Transfer Function'. Nothing will be done.";
return;
}
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
// pass the transfer function to our level window filter
localStorage->m_LevelWindowFilter->SetLookupTable(transferFunctionProp->GetValue()->GetColorTransferFunction());
localStorage->m_LevelWindowFilter->SetOpacityPiecewiseFunction(
transferFunctionProp->GetValue()->GetScalarOpacityFunction());
}
void mitk::ImageVtkMapper2D::SetToInvalidState(mitk::ImageVtkMapper2D::LocalStorage* localStorage)
{
localStorage->m_PublicActors = localStorage->m_EmptyActors.Get();
// set image to nullptr, to clear the texture in 3D, because
// the latest image is used there if the plane is out of the geometry
// see bug-13275
localStorage->m_ReslicedImage = nullptr;
localStorage->m_Mapper->SetInputData(localStorage->m_EmptyPolyData);
}
void mitk::ImageVtkMapper2D::Update(mitk::BaseRenderer *renderer)
{
bool visible = true;
GetDataNode()->GetVisibility(visible, renderer, "visible");
if (!visible)
{
return;
}
auto *data = const_cast<mitk::Image *>(this->GetInput());
if (data == nullptr)
{
return;
}
// Calculate time step of the input data for the specified renderer (integer value)
this->CalculateTimeStep(renderer);
LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer);
// Check if time step is valid
const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry();
if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) ||
(!dataTimeGeometry->IsValidTimeStep(this->GetTimestep())))
{
this->SetToInvalidState(localStorage);
return;
}
const DataNode *node = this->GetDataNode();
data->UpdateOutputInformation();
// check if something important has changed and we need to rerender
if ((localStorage->m_LastUpdateTime < node->GetMTime()) ||
(localStorage->m_LastUpdateTime < data->GetPipelineMTime()) ||
(localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) ||
(localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) ||
(localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) ||
(localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime()) ||
(localStorage->m_LastUpdateTime < data->GetPropertyList()->GetMTime()))
{
this->GenerateDataForRenderer(renderer);
}
// since we have checked that nothing important has changed, we can set
// m_LastUpdateTime to the current time
localStorage->m_LastUpdateTime.Modified();
}
void mitk::ImageVtkMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite)
{
mitk::Image::Pointer image = dynamic_cast<mitk::Image *>(node->GetData());
// Properties common for both images and segmentations
node->AddProperty("depthOffset", mitk::FloatProperty::New(0.0), renderer, overwrite);
node->AddProperty("outline binary", mitk::BoolProperty::New(false), renderer, overwrite);
node->AddProperty("outline width", mitk::FloatProperty::New(1.0), renderer, overwrite);
node->AddProperty("outline binary shadow", mitk::BoolProperty::New(false), renderer, overwrite);
node->AddProperty("outline binary shadow color", ColorProperty::New(0.0, 0.0, 0.0), renderer, overwrite);
node->AddProperty("outline shadow width", mitk::FloatProperty::New(1.5), renderer, overwrite);
if (image->IsRotated())
node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_CUBIC));
else
node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New());
node->AddProperty("texture interpolation", mitk::BoolProperty::New(false));
node->AddProperty("in plane resample extent by geometry", mitk::BoolProperty::New(false));
node->AddProperty("bounding box", mitk::BoolProperty::New(false));
mitk::RenderingModeProperty::Pointer renderingModeProperty = mitk::RenderingModeProperty::New();
node->AddProperty("Image Rendering.Mode", renderingModeProperty);
// Set default grayscale look-up table
mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New();
mitkLut->SetType(mitk::LookupTable::GRAYSCALE);
mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New();
mitkLutProp->SetLookupTable(mitkLut);
node->SetProperty("LookupTable", mitkLutProp, renderer);
std::string photometricInterpretation; // DICOM tag telling us how pixel values should be displayed
if (node->GetStringProperty("dicom.pixel.PhotometricInterpretation", photometricInterpretation))
{
// modality provided by DICOM or other reader
if (photometricInterpretation.find("MONOCHROME1") != std::string::npos) // meaning: display MINIMUM pixels as WHITE
{
// Set inverse grayscale look-up table
mitkLut->SetType(mitk::LookupTable::INVERSE_GRAYSCALE);
mitkLutProp->SetLookupTable(mitkLut);
node->SetProperty("LookupTable", mitkLutProp, renderer);
renderingModeProperty->SetValue(mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); // USE lookuptable
}
// Otherwise do nothing - the default grayscale look-up table has already been set
}
bool isBinaryImage(false);
if (!node->GetBoolProperty("binary", isBinaryImage) && image->GetPixelType().GetNumberOfComponents() == 1)
{
// ok, property is not set, use heuristic to determine if this
// is a binary image
mitk::Image::Pointer centralSliceImage;
mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New();
sliceSelector->SetInput(image);
sliceSelector->SetSliceNr(image->GetDimension(2) / 2);
sliceSelector->SetTimeNr(image->GetDimension(3) / 2);
sliceSelector->SetChannelNr(image->GetDimension(4) / 2);
sliceSelector->Update();
centralSliceImage = sliceSelector->GetOutput();
isBinaryImage = IsBinaryImage(centralSliceImage);
if (isBinaryImage) // Potential binary image. Now take a close look.
isBinaryImage = IsBinaryImage(image);
}
std::string className = image->GetNameOfClass();
if (className != "TensorImage" && className != "OdfImage" && className != "ShImage")
{
PixelType pixelType = image->GetPixelType();
size_t numComponents = pixelType.GetNumberOfComponents();
- if ((pixelType.GetPixelType() == itk::ImageIOBase::VECTOR && numComponents > 1) || numComponents == 2 ||
+ if ((pixelType.GetPixelType() == itk::IOPixelEnum::VECTOR && numComponents > 1) || numComponents == 2 ||
numComponents > 4)
{
node->AddProperty("Image.Displayed Component", mitk::IntProperty::New(0), renderer, overwrite);
}
}
// some more properties specific for a binary...
if (isBinaryImage)
{
node->AddProperty("opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite);
node->AddProperty("color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite);
node->AddProperty("binaryimage.selectedcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite);
node->AddProperty("binaryimage.selectedannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite);
node->AddProperty("binaryimage.hoveringcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite);
node->AddProperty("binaryimage.hoveringannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite);
node->AddProperty("binary", mitk::BoolProperty::New(true), renderer, overwrite);
node->AddProperty("layer", mitk::IntProperty::New(10), renderer, overwrite);
}
else //...or image type object
{
node->AddProperty("opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite);
node->AddProperty("color", ColorProperty::New(1.0, 1.0, 1.0), renderer, overwrite);
node->AddProperty("binary", mitk::BoolProperty::New(false), renderer, overwrite);
node->AddProperty("layer", mitk::IntProperty::New(0), renderer, overwrite);
}
if (image.IsNotNull() && image->IsInitialized())
{
if ((overwrite) || (node->GetProperty("levelwindow", renderer) == nullptr))
{
/* initialize level/window from DICOM tags */
mitk::LevelWindow contrast;
std::string sLevel = "";
std::string sWindow = "";
if (GetBackwardsCompatibleDICOMProperty(
0x0028, 0x1050, "dicom.voilut.WindowCenter", image->GetPropertyList(), sLevel) &&
GetBackwardsCompatibleDICOMProperty(
0x0028, 0x1051, "dicom.voilut.WindowWidth", image->GetPropertyList(), sWindow))
{
float level = atof(sLevel.c_str());
float window = atof(sWindow.c_str());
std::string sSmallestPixelValueInSeries;
std::string sLargestPixelValueInSeries;
if (GetBackwardsCompatibleDICOMProperty(0x0028,
0x0108,
"dicom.series.SmallestPixelValueInSeries",
image->GetPropertyList(),
sSmallestPixelValueInSeries) &&
GetBackwardsCompatibleDICOMProperty(0x0028,
0x0109,
"dicom.series.LargestPixelValueInSeries",
image->GetPropertyList(),
sLargestPixelValueInSeries))
{
float smallestPixelValueInSeries = atof(sSmallestPixelValueInSeries.c_str());
float largestPixelValueInSeries = atof(sLargestPixelValueInSeries.c_str());
contrast.SetRangeMinMax(smallestPixelValueInSeries - 1,
largestPixelValueInSeries + 1); // why not a little buffer?
// might remedy some l/w widget challenges
}
else
{
contrast.SetAuto(static_cast<mitk::Image *>(node->GetData()), false, true); // fallback
}
contrast.SetLevelWindow(level, window, true);
}
else
{
contrast.SetAuto(static_cast<mitk::Image *>(node->GetData()), false, true); // fallback
}
node->SetProperty("levelwindow", LevelWindowProperty::New(contrast), renderer);
}
if (((overwrite) || (node->GetProperty("opaclevelwindow", renderer) == nullptr)) &&
- (image->GetPixelType().GetPixelType() == itk::ImageIOBase::RGBA) &&
- (image->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR))
+ (image->GetPixelType().GetPixelType() == itk::IOPixelEnum::RGBA) &&
+ (image->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR))
{
mitk::LevelWindow opaclevwin;
opaclevwin.SetRangeMinMax(0, 255);
opaclevwin.SetWindowBounds(0, 255);
mitk::LevelWindowProperty::Pointer prop = mitk::LevelWindowProperty::New(opaclevwin);
node->SetProperty("opaclevelwindow", prop, renderer);
}
}
Superclass::SetDefaultProperties(node, renderer, overwrite);
}
mitk::ImageVtkMapper2D::LocalStorage *mitk::ImageVtkMapper2D::GetLocalStorage(mitk::BaseRenderer *renderer)
{
return m_LSH.GetLocalStorage(renderer);
}
const mitk::ImageVtkMapper2D::LocalStorage* mitk::ImageVtkMapper2D::GetConstLocalStorage(mitk::BaseRenderer* renderer)
{
return m_LSH.GetLocalStorage(renderer);
}
template <typename TPixel>
vtkSmartPointer<vtkPolyData> mitk::ImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = this->GetLocalStorage(renderer);
// get the min and max index values of each direction
int *extent = localStorage->m_ReslicedImage->GetExtent();
int xMin = extent[0];
int xMax = extent[1];
int yMin = extent[2];
int yMax = extent[3];
int *dims = localStorage->m_ReslicedImage->GetDimensions(); // dimensions of the image
int line = dims[0]; // how many pixels per line?
int x = xMin; // pixel index x
int y = yMin; // pixel index y
// get the depth for each contour
float depth = CalculateLayerDepth(renderer);
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New(); // the points to draw
vtkSmartPointer<vtkCellArray> lines = vtkSmartPointer<vtkCellArray>::New(); // the lines to connect the points
// We take the pointer to the first pixel of the image
auto* currentPixel = static_cast<TPixel*>(localStorage->m_ReslicedImage->GetScalarPointer());
while (y <= yMax)
{
// if the current pixel value is set to something
if ((currentPixel) && (*currentPixel != 0))
{
// check in which direction a line is necessary
// a line is added if the neighbor of the current pixel has the value 0
// and if the pixel is located at the edge of the image
// if vvvvv not the first line vvvvv
if (y > yMin && *(currentPixel - line) == 0)
{ // x direction - bottom edge of the pixel
// add the 2 points
vtkIdType p1 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 =
points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
// add the line between both points
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
}
// if vvvvv not the last line vvvvv
if (y < yMax && *(currentPixel + line) == 0)
{ // x direction - top edge of the pixel
vtkIdType p1 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 = points->InsertNextPoint(
(x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
}
// if vvvvv not the first pixel vvvvv
if ((x > xMin || y > yMin) && *(currentPixel - 1) == 0)
{ // y direction - left edge of the pixel
vtkIdType p1 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
}
// if vvvvv not the last pixel vvvvv
if ((y < yMax || (x < xMax)) && *(currentPixel + 1) == 0)
{ // y direction - right edge of the pixel
vtkIdType p1 =
points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 = points->InsertNextPoint(
(x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
}
/* now consider pixels at the edge of the image */
// if vvvvv left edge of image vvvvv
if (x == xMin)
{ // draw left edge of the pixel
vtkIdType p1 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
}
// if vvvvv right edge of image vvvvv
if (x == xMax)
{ // draw right edge of the pixel
vtkIdType p1 =
points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 = points->InsertNextPoint(
(x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
}
// if vvvvv bottom edge of image vvvvv
if (y == yMin)
{ // draw bottom edge of the pixel
vtkIdType p1 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 =
points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
}
// if vvvvv top edge of image vvvvv
if (y == yMax)
{ // draw top edge of the pixel
vtkIdType p1 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 = points->InsertNextPoint(
(x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
}
} // end if currentpixel is set
x++;
if (x > xMax)
{ // reached end of line
x = xMin;
y++;
}
// Increase the pointer-position to the next pixel.
// This is safe, as the while-loop and the x-reset logic above makes
// sure we do not exceed the bounds of the image
currentPixel++;
} // end of while
// Create a polydata to store everything in
vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New();
// Add the points to the dataset
polyData->SetPoints(points);
// Add the lines to the dataset
polyData->SetLines(lines);
return polyData;
}
void mitk::ImageVtkMapper2D::TransformActor(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
// get the transformation matrix of the reslicer in order to render the slice as axial, coronal or saggital
vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
vtkSmartPointer<vtkMatrix4x4> matrix = localStorage->m_Reslicer->GetResliceAxes();
trans->SetMatrix(matrix);
// transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or saggital)
localStorage->m_ImageActor->SetUserTransform(trans);
// transform the origin to center based coordinates, because MITK is center based.
localStorage->m_ImageActor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0);
localStorage->m_ShadowOutlineActor->SetUserTransform(trans);
localStorage->m_ShadowOutlineActor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0);
}
bool mitk::ImageVtkMapper2D::RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry,
SlicedGeometry3D *imageGeometry)
{
// if either one of the two geometries is nullptr we return true
// for safety reasons
if (renderingGeometry == nullptr || imageGeometry == nullptr)
return true;
// get the distance for the first cornerpoint
ScalarType initialDistance = renderingGeometry->SignedDistance(imageGeometry->GetCornerPoint(0));
for (int i = 1; i < 8; i++)
{
mitk::Point3D cornerPoint = imageGeometry->GetCornerPoint(i);
// get the distance to the other cornerpoints
ScalarType distance = renderingGeometry->SignedDistance(cornerPoint);
// if it has not the same signing as the distance of the first point
if (initialDistance * distance < 0)
{
// we have an intersection and return true
return true;
}
}
// all distances have the same sign, no intersection and we return false
return false;
}
mitk::ImageVtkMapper2D::LocalStorage::~LocalStorage()
{
}
mitk::ImageVtkMapper2D::LocalStorage::LocalStorage()
: m_VectorComponentExtractor(vtkSmartPointer<vtkImageExtractComponents>::New())
{
m_LevelWindowFilter = vtkSmartPointer<vtkMitkLevelWindowFilter>::New();
// Do as much actions as possible in here to avoid double executions.
m_Plane = vtkSmartPointer<vtkPlaneSource>::New();
m_Texture = vtkSmartPointer<vtkNeverTranslucentTexture>::New().GetPointer();
m_DefaultLookupTable = vtkSmartPointer<vtkLookupTable>::New();
m_BinaryLookupTable = vtkSmartPointer<vtkLookupTable>::New();
m_ColorLookupTable = vtkSmartPointer<vtkLookupTable>::New();
m_Mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
m_ImageActor = vtkSmartPointer<vtkActor>::New();
m_ShadowOutlineActor = vtkSmartPointer<vtkActor>::New();
m_Actors = vtkSmartPointer<vtkPropAssembly>::New();
m_EmptyActors = vtkSmartPointer<vtkPropAssembly>::New();
m_Reslicer = mitk::ExtractSliceFilter::New();
m_TSFilter = vtkSmartPointer<vtkMitkThickSlicesFilter>::New();
m_OutlinePolyData = vtkSmartPointer<vtkPolyData>::New();
m_ReslicedImage = vtkSmartPointer<vtkImageData>::New();
m_EmptyPolyData = vtkSmartPointer<vtkPolyData>::New();
// the following actions are always the same and thus can be performed
// in the constructor for each image (i.e. the image-corresponding local storage)
m_TSFilter->ReleaseDataFlagOn();
mitk::LookupTable::Pointer mitkLUT = mitk::LookupTable::New();
// built a default lookuptable
mitkLUT->SetType(mitk::LookupTable::GRAYSCALE);
m_DefaultLookupTable = mitkLUT->GetVtkLookupTable();
mitkLUT->SetType(mitk::LookupTable::LEGACY_BINARY);
m_BinaryLookupTable = mitkLUT->GetVtkLookupTable();
mitkLUT->SetType(mitk::LookupTable::LEGACY_RAINBOW_COLOR);
m_ColorLookupTable = mitkLUT->GetVtkLookupTable();
// do not repeat the texture (the image)
m_Texture->RepeatOff();
// set the mapper for the actor
m_ImageActor->SetMapper(m_Mapper);
m_ShadowOutlineActor->SetMapper(m_Mapper);
m_Actors->AddPart(m_ShadowOutlineActor);
m_Actors->AddPart(m_ImageActor);
m_PublicActors = m_EmptyActors.Get();
}
diff --git a/Modules/Core/src/mitkCoreServices.cpp b/Modules/Core/src/mitkCoreServices.cpp
index 4470ec796c..acf5c6963f 100644
--- a/Modules/Core/src/mitkCoreServices.cpp
+++ b/Modules/Core/src/mitkCoreServices.cpp
@@ -1,128 +1,127 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkCoreServices.h"
#include <mitkIMimeTypeProvider.h>
#include <mitkIPropertyAliases.h>
#include <mitkIPropertyDescriptions.h>
#include <mitkIPropertyExtensions.h>
#include <mitkIPropertyFilters.h>
#include <mitkIPropertyPersistence.h>
#include <mitkIPropertyRelations.h>
#include <usGetModuleContext.h>
#include <usModuleContext.h>
#include <usServiceReference.h>
#include <usServiceTracker.h>
-#include <itkMutexLockHolder.h>
-#include <itkSimpleFastMutexLock.h>
+#include <mutex>
namespace mitk
{
- itk::SimpleFastMutexLock &s_ContextToServicesMapMutex()
+ std::mutex &s_ContextToServicesMapMutex()
{
- static itk::SimpleFastMutexLock mutex;
+ static std::mutex mutex;
return mutex;
}
std::map<us::ModuleContext *, std::map<void *, us::ServiceReferenceU>> &s_ContextToServicesMap()
{
static std::map<us::ModuleContext *, std::map<void *, us::ServiceReferenceU>> serviceMap;
return serviceMap;
}
template <class S>
static S *GetCoreService(us::ModuleContext *context)
{
if (context == nullptr)
context = us::GetModuleContext();
S *coreService = nullptr;
us::ServiceReference<S> serviceRef = context->GetServiceReference<S>();
if (serviceRef)
{
coreService = context->GetService(serviceRef);
}
assert(coreService && "Asserting non-nullptr MITK core service");
{
- itk::MutexLockHolder<itk::SimpleFastMutexLock> l(s_ContextToServicesMapMutex());
+ std::lock_guard<std::mutex> l(s_ContextToServicesMapMutex());
s_ContextToServicesMap()[context].insert(std::make_pair(coreService, serviceRef));
}
return coreService;
}
IPropertyAliases *CoreServices::GetPropertyAliases(us::ModuleContext *context)
{
return GetCoreService<IPropertyAliases>(context);
}
IPropertyDescriptions *CoreServices::GetPropertyDescriptions(us::ModuleContext *context)
{
return GetCoreService<IPropertyDescriptions>(context);
}
IPropertyExtensions *CoreServices::GetPropertyExtensions(us::ModuleContext *context)
{
return GetCoreService<IPropertyExtensions>(context);
}
IPropertyFilters *CoreServices::GetPropertyFilters(us::ModuleContext *context)
{
return GetCoreService<IPropertyFilters>(context);
}
IPropertyPersistence *CoreServices::GetPropertyPersistence(us::ModuleContext *context)
{
return GetCoreService<IPropertyPersistence>(context);
}
IPropertyRelations *CoreServices::GetPropertyRelations(us::ModuleContext *context)
{
return GetCoreService<IPropertyRelations>(context);
}
IMimeTypeProvider *CoreServices::GetMimeTypeProvider(us::ModuleContext *context)
{
return GetCoreService<IMimeTypeProvider>(context);
}
bool CoreServices::Unget(us::ModuleContext *context, const std::string & /*interfaceId*/, void *service)
{
bool success = false;
- itk::MutexLockHolder<itk::SimpleFastMutexLock> l(s_ContextToServicesMapMutex());
+ std::lock_guard<std::mutex> l(s_ContextToServicesMapMutex());
auto iter =
s_ContextToServicesMap().find(context);
if (iter != s_ContextToServicesMap().end())
{
auto iter2 = iter->second.find(service);
if (iter2 != iter->second.end())
{
us::ServiceReferenceU serviceRef = iter2->second;
if (serviceRef)
{
success = context->UngetService(serviceRef);
if (success)
{
iter->second.erase(iter2);
}
}
}
}
return success;
}
}
diff --git a/Modules/Core/test/mitkArbitraryTimeGeometryTest.cpp b/Modules/Core/test/mitkArbitraryTimeGeometryTest.cpp
index 4935c25e5d..68f3ed89c5 100644
--- a/Modules/Core/test/mitkArbitraryTimeGeometryTest.cpp
+++ b/Modules/Core/test/mitkArbitraryTimeGeometryTest.cpp
@@ -1,532 +1,531 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkArbitraryTimeGeometry.h"
#include "mitkGeometry3D.h"
#include "mitkTestFixture.h"
#include "mitkTestingMacros.h"
#include <limits>
class mitkArbitraryTimeGeometryTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkArbitraryTimeGeometryTestSuite);
// Test the append method
MITK_TEST(CountTimeSteps);
MITK_TEST(GetMinimumTimePoint);
MITK_TEST(GetMaximumTimePoint);
MITK_TEST(GetTimeBounds);
MITK_TEST(IsValidTimePoint);
MITK_TEST(TimeStepToTimePoint);
MITK_TEST(TimePointToTimeStep);
MITK_TEST(GetGeometryCloneForTimeStep);
MITK_TEST(GetGeometryForTimeStep);
MITK_TEST(GetGeometryForTimePoint);
MITK_TEST(IsValid);
MITK_TEST(Expand);
MITK_TEST(ReplaceTimeStepGeometries);
MITK_TEST(ClearAllGeometries);
MITK_TEST(AppendNewTimeStep);
MITK_TEST(HasCollapsedFinalTimeStep);
CPPUNIT_TEST_SUITE_END();
private:
mitk::Geometry3D::Pointer m_Geometry1;
mitk::Geometry3D::Pointer m_Geometry2;
mitk::Geometry3D::Pointer m_Geometry3;
mitk::Geometry3D::Pointer m_Geometry3_5;
mitk::Geometry3D::Pointer m_Geometry4;
mitk::Geometry3D::Pointer m_Geometry5;
mitk::Geometry3D::Pointer m_InvalidGeometry;
mitk::Geometry3D::Pointer m_NewGeometry;
mitk::TimePointType m_Geometry1MinTP;
mitk::TimePointType m_Geometry2MinTP;
mitk::TimePointType m_Geometry3MinTP;
mitk::TimePointType m_Geometry3_5MinTP;
mitk::TimePointType m_Geometry4MinTP;
mitk::TimePointType m_Geometry5MinTP;
mitk::TimePointType m_NewGeometryMinTP;
mitk::TimePointType m_Geometry1MaxTP;
mitk::TimePointType m_Geometry2MaxTP;
mitk::TimePointType m_Geometry3MaxTP;
mitk::TimePointType m_Geometry3_5MaxTP;
mitk::TimePointType m_Geometry4MaxTP;
mitk::TimePointType m_Geometry5MaxTP;
mitk::TimePointType m_NewGeometryMaxTP;
mitk::ArbitraryTimeGeometry::Pointer m_emptyTimeGeometry;
mitk::ArbitraryTimeGeometry::Pointer m_initTimeGeometry;
mitk::ArbitraryTimeGeometry::Pointer m_12345TimeGeometry;
mitk::ArbitraryTimeGeometry::Pointer m_123TimeGeometry;
mitk::ArbitraryTimeGeometry::Pointer m_123TimeGeometryWithCollapsedEnd;
mitk::ArbitraryTimeGeometry::Pointer m_123TimeGeometryWithCollapsedInterim;
public:
void setUp() override
{
- mitk::TimeBounds bounds;
m_Geometry1 = mitk::Geometry3D::New();
m_Geometry2 = mitk::Geometry3D::New();
m_Geometry3 = mitk::Geometry3D::New();
m_Geometry3_5 = mitk::Geometry3D::New();
m_Geometry4 = mitk::Geometry3D::New();
m_Geometry5 = mitk::Geometry3D::New();
m_Geometry1MinTP = 1;
m_Geometry2MinTP = 2;
m_Geometry3MinTP = 3;
m_Geometry3_5MinTP = 3.5;
m_Geometry4MinTP = 4;
m_Geometry5MinTP = 5;
m_Geometry1MaxTP = 1.9;
m_Geometry2MaxTP = 2.9;
m_Geometry3MaxTP = 3.9;
m_Geometry3_5MaxTP = 3.9;
m_Geometry4MaxTP = 4.9;
m_Geometry5MaxTP = 5.9;
m_NewGeometry = mitk::Geometry3D::New();
m_NewGeometryMinTP = 20;
m_NewGeometryMaxTP = 21.9;
mitk::Point3D origin(42);
m_NewGeometry->SetOrigin(origin);
m_emptyTimeGeometry = mitk::ArbitraryTimeGeometry::New();
m_emptyTimeGeometry->ClearAllGeometries();
m_initTimeGeometry = mitk::ArbitraryTimeGeometry::New();
m_initTimeGeometry->Initialize();
m_12345TimeGeometry = mitk::ArbitraryTimeGeometry::New();
m_12345TimeGeometry->ClearAllGeometries();
m_12345TimeGeometry->AppendNewTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP);
m_12345TimeGeometry->AppendNewTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MaxTP);
m_12345TimeGeometry->AppendNewTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MaxTP);
m_12345TimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MinTP, m_Geometry4MaxTP);
m_12345TimeGeometry->AppendNewTimeStep(m_Geometry5, m_Geometry5MinTP, m_Geometry5MaxTP);
m_123TimeGeometry = mitk::ArbitraryTimeGeometry::New();
m_123TimeGeometry->ClearAllGeometries();
m_123TimeGeometry->AppendNewTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP);
m_123TimeGeometry->AppendNewTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MaxTP);
m_123TimeGeometry->AppendNewTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MaxTP);
m_123TimeGeometryWithCollapsedEnd = mitk::ArbitraryTimeGeometry::New();
m_123TimeGeometryWithCollapsedEnd->ClearAllGeometries();
m_123TimeGeometryWithCollapsedEnd->AppendNewTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP);
m_123TimeGeometryWithCollapsedEnd->AppendNewTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MaxTP);
m_123TimeGeometryWithCollapsedEnd->AppendNewTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MinTP);
m_123TimeGeometryWithCollapsedInterim = mitk::ArbitraryTimeGeometry::New();
m_123TimeGeometryWithCollapsedInterim->ClearAllGeometries();
m_123TimeGeometryWithCollapsedInterim->AppendNewTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP);
m_123TimeGeometryWithCollapsedInterim->AppendNewTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MinTP);
m_123TimeGeometryWithCollapsedInterim->AppendNewTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MaxTP);
}
void tearDown() override {}
void CountTimeSteps()
{
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->CountTimeSteps() == 0,
"Testing CountTimeSteps with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->CountTimeSteps() == 1,
"Testing CountTimeSteps with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 5,
"Testing CountTimeSteps with m_12345TimeGeometry");
}
void GetMinimumTimePoint()
{
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMinimumTimePoint() == 0.0,
"Testing GetMinimumTimePoint with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMinimumTimePoint() == 0.0,
"Testing GetMinimumTimePoint with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMinimumTimePoint() == 1.0,
"Testing GetMinimumTimePoint with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMinimumTimePoint(2) == 0.0,
"Testing GetMinimumTimePoint(2) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMinimumTimePoint(2) == 0.0,
"Testing GetMinimumTimePoint(2) with m_initTimeGeometry");
///////////////////////////////////////
// Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details.
// This workarround should be removed/reevaluated as soon as T28262 is solved and we know
// how time geometries should behave in the future!
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMinimumTimePoint(2) == 3.0,
"Testing GetMinimumTimePoint(2) with m_12345TimeGeometry");
// Deactivated falling original test
// MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMinimumTimePoint(2) == 2.9,
// "Testing GetMinimumTimePoint(2) with m_12345TimeGeometry");
// End of workarround for T27883
//////////////////////////////////////
}
void GetMaximumTimePoint()
{
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMaximumTimePoint() == 0.0,
"Testing GetMaximumTimePoint with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMaximumTimePoint() == 1.0,
"Testing GetMaximumTimePoint with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMaximumTimePoint() == 5.9,
"Testing GetMaximumTimePoint with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMaximumTimePoint(2) == 0.0,
"Testing GetMaximumTimePoint(2) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMaximumTimePoint(2) == 0.0,
"Testing GetMaximumTimePoint(2) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMaximumTimePoint(2) == 3.9,
"Testing GetMaximumTimePoint(2) with m_12345TimeGeometry");
}
void GetTimeBounds()
{
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMaximumTimePoint(2) == 0.0,
"Testing GetMaximumTimePoint(2) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMaximumTimePoint(2) == 0.0,
"Testing GetMaximumTimePoint(2) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMaximumTimePoint(2) == 3.9,
"Testing GetMaximumTimePoint(2) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetTimeBounds()[0] == 0.0,
"Testing GetTimeBounds lower part with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetTimeBounds()[0] == 0.0,
"Testing GetTimeBounds lower part with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds()[0] == 1.0,
"Testing GetTimeBounds lower part with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetTimeBounds()[1] == 0.0,
"Testing GetTimeBounds with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetTimeBounds()[1] == 1.0,
"Testing GetTimeBounds with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds()[1] == 5.9,
"Testing GetTimeBounds with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetTimeBounds(3)[0] == 0.0,
"Testing GetTimeBounds(3) lower part with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetTimeBounds(3)[0] == 0.0,
"Testing GetTimeBounds(3) lower part with m_initTimeGeometry");
///////////////////////////////////////
// Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details.
// This workarround should be removed/reevaluated as soon as T28262 is solved and we know
// how time geometries should behave in the future!
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds(3)[0] == 4.0,
"Testing GetTimeBounds(3) lower part with m_12345TimeGeometry");
// Deactivated falling original test
// MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds(3)[0] == 3.9,
// "Testing GetTimeBounds(3) lower part with m_12345TimeGeometry");
// End of workarround for T27883
//////////////////////////////////////
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetTimeBounds(3)[1] == 0.0,
"Testing GetTimeBounds(3) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetTimeBounds(3)[1] == 0.0,
"Testing GetTimeBounds(3) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds(3)[1] == 4.9,
"Testing GetTimeBounds(3) with m_12345TimeGeometry");
}
void IsValidTimePoint()
{
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(-1) == false,
"Testing IsValidTimePoint(-1) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(-1) == false,
"Testing IsValidTimePoint(-1) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(-1) == false,
"Testing IsValidTimePoint(-1) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(0) == false,
"Testing IsValidTimePoint(0) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(0) == true,
"Testing IsValidTimePoint(0) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(0) == false,
"Testing IsValidTimePoint(0) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(1) == false,
"Testing IsValidTimePoint(1) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(1) == false,
"Testing IsValidTimePoint(1) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(1) == true,
"Testing IsValidTimePoint(1) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(2.5) == false,
"Testing IsValidTimePoint(2.5) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(2.5) == false,
"Testing IsValidTimePoint(2.5) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(2.5) == true,
"Testing IsValidTimePoint(2.5) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(5.89) == false,
"Testing IsValidTimePoint(5.89) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(5.89) == false,
"Testing IsValidTimePoint(5.89) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(5.89) == true,
"Testing IsValidTimePoint(5.89) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(10) == false,
"Testing IsValidTimePoint(10) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(10) == false,
"Testing IsValidTimePoint(10) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(10) == false,
"Testing IsValidTimePoint(10) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimeStep(0) == false,
"Testing IsValidTimeStep(0) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimeStep(0) == true,
"Testing IsValidTimeStep(0) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimeStep(0) == true,
"Testing IsValidTimeStep(0) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimeStep(1) == false,
"Testing IsValidTimeStep(1) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimeStep(1) == false,
"Testing IsValidTimeStep(1) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimeStep(1) == true,
"Testing IsValidTimeStep(1) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimeStep(6) == false,
"Testing IsValidTimeStep(6) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimeStep(6) == false,
"Testing IsValidTimeStep(6) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimeStep(6) == false,
"Testing IsValidTimeStep(6) with m_12345TimeGeometry");
//checked collapsed cases
MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedInterim->IsValidTimePoint(m_123TimeGeometryWithCollapsedInterim->GetMaximumTimePoint()) == false,
"Testing that m_123TimeGeometryWithCollapsedInterim does not inclued the max bound in validity");
MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedEnd->IsValidTimePoint(m_123TimeGeometryWithCollapsedEnd->GetMaximumTimePoint()) == true,
"Testing that m_123TimeGeometryWithCollapsedEnd does inclued the max bound in validity, because it has an collapsed final time step. (see also T27259)");
}
void TimeStepToTimePoint()
{
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimeStepToTimePoint(0) == 0.0,
"Testing TimeStepToTimePoint(0) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimeStepToTimePoint(0) == 0.0,
"Testing TimeStepToTimePoint(0) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimeStepToTimePoint(0) == 1.0,
"Testing TimeStepToTimePoint(0) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimeStepToTimePoint(1) == 0.0,
"Testing TimeStepToTimePoint(1) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimeStepToTimePoint(1) == 0.0,
"Testing TimeStepToTimePoint(1) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimeStepToTimePoint(1) == 2.0,
"Testing TimeStepToTimePoint(1) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimeStepToTimePoint(6) == 0.0,
"Testing TimeStepToTimePoint(6) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimeStepToTimePoint(6) == 0.0,
"Testing TimeStepToTimePoint(6) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimeStepToTimePoint(6) == 0.0,
"Testing TimeStepToTimePoint(6) with m_12345TimeGeometry");
}
void TimePointToTimeStep()
{
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimePointToTimeStep(0.0) == 0,
"Testing TimePointToTimeStep(0.0) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimePointToTimeStep(0.0) == 0,
"Testing TimePointToTimeStep(0.0) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(0.0) == 0,
"Testing TimePointToTimeStep(0.0) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimePointToTimeStep(0.5) == 0,
"Testing TimePointToTimeStep(0.5) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimePointToTimeStep(0.5) == 0,
"Testing TimePointToTimeStep(0.5) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(0.5) == 0,
"Testing TimePointToTimeStep(0.5) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimePointToTimeStep(3.5) == 0,
"Testing TimePointToTimeStep(3.5) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimePointToTimeStep(3.5) == m_initTimeGeometry->CountTimeSteps(),
"Testing TimePointToTimeStep(3.5) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(3.5) == 2,
"Testing TimePointToTimeStep(3.5) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimePointToTimeStep(5.8) == 0,
"Testing TimePointToTimeStep(5.8) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimePointToTimeStep(5.8) == m_initTimeGeometry->CountTimeSteps(),
"Testing TimePointToTimeStep(5.8) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(5.8) == 4,
"Testing TimePointToTimeStep(5.8) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(5.9) == m_12345TimeGeometry->CountTimeSteps(),
"Testing TimePointToTimeStep(5.9) with m_12345TimeGeometry");
//checked collapsed cases
MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedInterim->TimePointToTimeStep(m_123TimeGeometryWithCollapsedInterim->GetMaximumTimePoint()) == m_123TimeGeometryWithCollapsedInterim->CountTimeSteps(),
"Testing m_123TimeGeometryWithCollapsedInterim does not map the max time poit.");
MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedEnd->TimePointToTimeStep(m_123TimeGeometryWithCollapsedEnd->GetMaximumTimePoint()) == 2,
"Testing that m_123TimeGeometryWithCollapsedEnd does map the max bound, because it has an collapsed final time step. (see also T27259)");
}
void GetGeometryCloneForTimeStep()
{
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetGeometryCloneForTimeStep(0).IsNull(),
"Testing GetGeometryCloneForTimeStep(0) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryCloneForTimeStep(0).IsNotNull(),
"Testing GetGeometryCloneForTimeStep(0) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetGeometryCloneForTimeStep(0).IsNotNull(),
"Testing GetGeometryCloneForTimeStep(0) with m_12345TimeGeometry");
}
void GetGeometryForTimeStep()
{
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetGeometryForTimeStep(0).IsNull(),
"Testing GetGeometryForTimePoint(0) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryForTimeStep(0).IsNotNull(),
"Testing GetGeometryForTimePoint(0) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryForTimeStep(1).IsNull(),
"Testing GetGeometryForTimePoint(1) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(
m_12345TimeGeometry->GetGeometryForTimeStep(0).GetPointer() == m_Geometry1.GetPointer(),
"Testing GetGeometryForTimePoint(0) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(
m_12345TimeGeometry->GetGeometryForTimeStep(3).GetPointer() == m_Geometry4.GetPointer(),
"Testing GetGeometryForTimePoint(3) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(
m_12345TimeGeometry->GetGeometryForTimeStep(4).GetPointer() == m_Geometry5.GetPointer(),
"Testing GetGeometryForTimePoint(4) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetGeometryForTimeStep(5).IsNull(),
"Testing GetGeometryForTimePoint(5) with m_12345TimeGeometry");
}
void GetGeometryForTimePoint()
{
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetGeometryForTimePoint(0).IsNull(),
"Testing GetGeometryForTimeStep(0) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryForTimePoint(0).IsNotNull(),
"Testing GetGeometryForTimeStep(0) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetGeometryForTimePoint(0).IsNull(),
"Testing GetGeometryForTimeStep(0) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetGeometryForTimePoint(1.5).IsNull(),
"Testing GetGeometryForTimeStep(1.5) with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryForTimePoint(1.5).IsNull(),
"Testing GetGeometryForTimeStep(1.5) with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(
m_12345TimeGeometry->GetGeometryForTimePoint(1.5).GetPointer() == m_Geometry1.GetPointer(),
"Testing GetGeometryForTimeStep(1.5) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(
m_12345TimeGeometry->GetGeometryForTimePoint(3.5).GetPointer() == m_Geometry3.GetPointer(),
"Testing GetGeometryForTimeStep(3.5) with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetGeometryForTimePoint(5.9).IsNull(),
"Testing GetGeometryForTimeStep(5.9) with m_12345TimeGeometry");
}
void IsValid()
{
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValid() == false, "Testing IsValid() with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValid() == true, "Testing IsValid() with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValid() == true, "Testing IsValid() with m_12345TimeGeometry");
}
void Expand()
{
m_12345TimeGeometry->Expand(3);
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 5,
"Testing Expand(3) doesn't change m_12345TimeGeometry");
m_12345TimeGeometry->Expand(7);
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 7,
"Testing Expand(7) with m_12345TimeGeometry");
}
void ReplaceTimeStepGeometries()
{
// Test replace time step geometries
m_12345TimeGeometry->ReplaceTimeStepGeometries(m_NewGeometry);
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 5,
"Testing ReplaceTimeStepGeometries() with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(
m_12345TimeGeometry->GetGeometryForTimeStep(0)->GetOrigin() == m_NewGeometry->GetOrigin(),
"Testing ReplaceTimeStepGeometries(): check if first geometry of m_12345TimeGeometry "
"was replaced m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(
m_12345TimeGeometry->GetGeometryForTimeStep(4)->GetOrigin() == m_NewGeometry->GetOrigin(),
"Testing ReplaceTimeStepGeometries(): check if last geometry of m_12345TimeGeometry "
"was replaced m_12345TimeGeometry");
}
void ClearAllGeometries()
{
// Test clear all geometries
m_12345TimeGeometry->ClearAllGeometries();
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 0,
"Testing ClearAllGeometries() with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMinimumTimePoint() == 0,
"Testing ClearAllGeometries() with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMaximumTimePoint() == 0,
"Testing ClearAllGeometries() with m_12345TimeGeometry");
}
void AppendNewTimeStep()
{
// Test append
MITK_TEST_FOR_EXCEPTION(mitk::Exception, m_123TimeGeometry->AppendNewTimeStep(nullptr, 0, 1));
MITK_TEST_FOR_EXCEPTION(mitk::Exception, m_123TimeGeometry->AppendNewTimeStep(m_Geometry3_5,m_Geometry3_5MinTP,m_Geometry3_5MaxTP));
MITK_TEST_FOR_EXCEPTION(mitk::Exception, m_123TimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MaxTP, m_Geometry4MinTP)); //valid but inverted bounds
m_emptyTimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MinTP, m_Geometry4MaxTP);
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->CountTimeSteps() == 1,
"Testing AppendNewTimeStep() with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMinimumTimePoint() == 4,
"Testing ClearAllGeometries() with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMaximumTimePoint() == 4.9,
"Testing ClearAllGeometries() with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->CountTimeSteps() == 3,
"Testing AppendNewTimeStep() with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMinimumTimePoint() == 1,
"Testing ClearAllGeometries() with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMaximumTimePoint() == 3.9,
"Testing ClearAllGeometries() with m_emptyTimeGeometry");
m_123TimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MinTP, m_Geometry4MaxTP);
MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->CountTimeSteps() == 4,
"Testing AppendNewTimeStep() with m_123TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMinimumTimePoint() == 1,
"Testing AppendNewTimeStep() with m_123TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMaximumTimePoint() == 4.9,
"Testing AppendNewTimeStep() with m_123TimeGeometry");
///////////////////////////////////////
// Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details.
// This workarround should be removed/reevaluated as soon as T28262 is solved and we know
// how time geometries should behave in the future!
MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMinimumTimePoint(3) == 4.0,
"Testing AppendNewTimeStep() with m_123TimeGeometry");
// Deactivated falling original test
// MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMinimumTimePoint(3) == 3.9,
// "Testing AppendNewTimeStep() with m_123TimeGeometry");
// End of workarround for T27883
//////////////////////////////////////
}
void HasCollapsedFinalTimeStep()
{
MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->HasCollapsedFinalTimeStep() == false, "Testing HasCollapsedFinalTimeStep() with m_emptyTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->HasCollapsedFinalTimeStep() == false, "Testing HasCollapsedFinalTimeStep() with m_initTimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->HasCollapsedFinalTimeStep() == false, "Testing HasCollapsedFinalTimeStep() with m_12345TimeGeometry");
MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedEnd->HasCollapsedFinalTimeStep() == true, "Testing HasCollapsedFinalTimeStep() with m_123TimeGeometryWithCollapsedEnd");
MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedInterim->HasCollapsedFinalTimeStep() == false, "Testing HasCollapsedFinalTimeStep() with m_123TimeGeometryWithCollapsedInterim");
}
};
MITK_TEST_SUITE_REGISTRATION(mitkArbitraryTimeGeometry)
diff --git a/Modules/Core/test/mitkBaseGeometryTest.cpp b/Modules/Core/test/mitkBaseGeometryTest.cpp
index f4907791b2..b975f48661 100644
--- a/Modules/Core/test/mitkBaseGeometryTest.cpp
+++ b/Modules/Core/test/mitkBaseGeometryTest.cpp
@@ -1,1681 +1,1680 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkTestingMacros.h"
#include <mitkTestFixture.h>
#include <mitkTestingConfig.h>
#include <MitkCoreExports.h>
#include <mitkBaseGeometry.h>
#include <mitkCommon.h>
#include <mitkOperationActor.h>
-#include <itkAffineGeometryFrame.h>
#include <itkBoundingBox.h>
#include <itkIndex.h>
#include <itkQuaternionRigidTransform.h>
#include <itkScalableAffineTransform.h>
#include <mitkVector.h>
#include <vtkMatrix4x4.h>
#include <vtkMatrixToLinearTransform.h>
#include <mitkImageCast.h>
#include <mitkInteractionConst.h>
#include <mitkMatrixConvert.h>
#include <mitkRotationOperation.h>
#include <mitkScaleOperation.h>
#include <mitkNodePredicateGeometry.h>
class vtkMatrix4x4;
class vtkMatrixToLinearTransform;
class vtkLinearTransform;
typedef itk::BoundingBox<unsigned long, 3, mitk::ScalarType> BoundingBox;
typedef itk::BoundingBox<unsigned long, 3, mitk::ScalarType> BoundingBoxType;
typedef BoundingBoxType::BoundsArrayType BoundsArrayType;
typedef BoundingBoxType::Pointer BoundingBoxPointer;
// Dummy instance of abstract base class
class DummyTestClass : public mitk::BaseGeometry
{
public:
DummyTestClass(){};
DummyTestClass(const DummyTestClass &other) : BaseGeometry(other){};
~DummyTestClass() override{};
mitkClassMacro(DummyTestClass, mitk::BaseGeometry);
itkNewMacro(Self);
mitkNewMacro1Param(Self, const Self &);
itk::LightObject::Pointer InternalClone() const override
{
Self::Pointer newGeometry = new Self(*this);
newGeometry->UnRegister();
return newGeometry.GetPointer();
}
protected:
void PrintSelf(std::ostream & /*os*/, itk::Indent /*indent*/) const override{};
//##Documentation
//## @brief Pre- and Post-functions are empty in BaseGeometry
//##
//## These virtual functions allow for a different beahiour in subclasses.
//## Do implement them in every subclass of BaseGeometry. If not needed, use {}.
//## If this class is inherited from a subclass of BaseGeometry, call {Superclass::Pre...();};, example:
// SlicedGeometry3D class
void PreSetSpacing(const mitk::Vector3D &/*aSpacing*/) override{};
};
class mitkBaseGeometryTestSuite : public mitk::TestFixture
{
// List of Tests
CPPUNIT_TEST_SUITE(mitkBaseGeometryTestSuite);
// Constructor
MITK_TEST(TestConstructors);
MITK_TEST(TestInitialize);
// Set
MITK_TEST(TestSetOrigin);
MITK_TEST(TestSetBounds);
MITK_TEST(TestSetFloatBounds);
MITK_TEST(TestSetFloatBoundsDouble);
MITK_TEST(TestSetFrameOfReferenceID);
MITK_TEST(TestSetIndexToWorldTransform);
MITK_TEST(TestSetIndexToWorldTransformWithoutChangingSpacing);
MITK_TEST(TestSetIndexToWorldTransform_WithPointerToSameTransform);
MITK_TEST(TestSetSpacing);
MITK_TEST(TestTransferItkToVtkTransform);
MITK_TEST(TestSetIndexToWorldTransformByVtkMatrix);
MITK_TEST(TestSetIdentity);
MITK_TEST(TestSetImageGeometry);
// Equal
MITK_TEST(Equal_CloneAndOriginal_ReturnsTrue);
MITK_TEST(Equal_DifferentOrigin_ReturnsFalse);
MITK_TEST(Equal_DifferentIndexToWorldTransform_ReturnsFalse);
MITK_TEST(Equal_DifferentSpacing_ReturnsFalse);
MITK_TEST(Equal_InputIsNull_ReturnsFalse);
MITK_TEST(Equal_DifferentBoundingBox_ReturnsFalse);
MITK_TEST(Equal_Transforms_MinorDifferences_And_Eps);
// other Functions
MITK_TEST(TestComposeTransform);
MITK_TEST(TestComposeVtkMatrix);
MITK_TEST(TestTranslate);
MITK_TEST(TestIndexToWorld);
MITK_TEST(TestExecuteOperation);
MITK_TEST(TestCalculateBoundingBoxRelToTransform);
// MITK_TEST(TestSetTimeBounds);
MITK_TEST(TestIs2DConvertable);
MITK_TEST(TestGetCornerPoint);
MITK_TEST(TestExtentInMM);
MITK_TEST(TestGetAxisVector);
MITK_TEST(TestGetCenter);
MITK_TEST(TestGetDiagonalLength);
MITK_TEST(TestGetExtent);
MITK_TEST(TestIsInside);
MITK_TEST(TestGetMatrixColumn);
// test IsSubGeometry
MITK_TEST(IsSubGeometry_Spacing);
MITK_TEST(IsSubGeometry_TransformMatrix);
MITK_TEST(IsSubGeometry_Bounds_Image);
MITK_TEST(IsSubGeometry_Bounds_NoneImage);
MITK_TEST(IsSubGeometry_Grid_Image);
MITK_TEST(IsSubGeometry_Grid_NoneImage);
MITK_TEST(IsSubGeometry_Bounds_Oblique_Image);
MITK_TEST(IsSubGeometry_Bounds_Oblique_NoneImage);
MITK_TEST(IsSubGeometry_Grid_Oblique_Image);
MITK_TEST(IsSubGeometry_Grid_Oblique_NoneImage);
CPPUNIT_TEST_SUITE_END();
// Used Variables
private:
mitk::Point3D aPoint;
float aFloatSpacing[3];
mitk::Vector3D aSpacing;
mitk::AffineTransform3D::Pointer aTransform;
BoundingBoxPointer aBoundingBox;
mitk::AffineTransform3D::MatrixType aMatrix;
mitk::Point3D anotherPoint;
mitk::Vector3D anotherSpacing;
BoundingBoxPointer anotherBoundingBox;
BoundingBoxPointer aThirdBoundingBox;
mitk::AffineTransform3D::Pointer anotherTransform;
mitk::AffineTransform3D::Pointer aThirdTransform;
mitk::AffineTransform3D::MatrixType anotherMatrix;
mitk::AffineTransform3D::MatrixType aThirdMatrix;
DummyTestClass::Pointer aDummyGeometry;
DummyTestClass::Pointer anotherDummyGeometry;
DummyTestClass::Pointer aDummyGeometryOblique;
public:
// Set up for variables
void setUp() override
{
mitk::FillVector3D(aFloatSpacing, 1, 1, 1);
mitk::FillVector3D(aSpacing, 1, 1, 1);
mitk::FillVector3D(aPoint, 0, 0, 0);
// Transform
aTransform = mitk::AffineTransform3D::New();
aTransform->SetIdentity();
aMatrix.SetIdentity();
anotherTransform = mitk::AffineTransform3D::New();
anotherMatrix.SetIdentity();
anotherMatrix(1, 1) = 2;
anotherTransform->SetMatrix(anotherMatrix);
aThirdTransform = mitk::AffineTransform3D::New();
aThirdMatrix.SetIdentity();
aThirdMatrix(1, 1) = 7;
aThirdTransform->SetMatrix(aThirdMatrix);
// Bounding Box
float bounds[6] = { 0, 1, 0, 1, 0, 1 };
mitk::BoundingBox::BoundsArrayType b;
const float* input = bounds;
int j = 0;
for (mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); j < 6; ++j)
*it++ = (mitk::ScalarType) * input++;
aBoundingBox = BoundingBoxType::New();
BoundingBoxType::PointsContainer::Pointer pointscontainer = BoundingBoxType::PointsContainer::New();
BoundingBoxType::PointType p;
BoundingBoxType::PointIdentifier pointid;
for (pointid = 0; pointid < 2; ++pointid)
{
unsigned int i;
for (i = 0; i < 3; ++i)
{
p[i] = bounds[2 * i + pointid];
}
pointscontainer->InsertElement(pointid, p);
}
aBoundingBox->SetPoints(pointscontainer);
aBoundingBox->ComputeBoundingBox();
anotherBoundingBox = BoundingBoxType::New();
p[0] = 11;
p[1] = 12;
p[2] = 13;
pointscontainer->InsertElement(1, p);
anotherBoundingBox->SetPoints(pointscontainer);
anotherBoundingBox->ComputeBoundingBox();
aThirdBoundingBox = BoundingBoxType::New();
p[0] = 22;
p[1] = 23;
p[2] = 24;
pointscontainer->InsertElement(1, p);
aThirdBoundingBox->SetPoints(pointscontainer);
aThirdBoundingBox->ComputeBoundingBox();
mitk::FillVector3D(anotherPoint, 2, 3, 4);
mitk::FillVector3D(anotherSpacing, 5, 6.5, 7);
aDummyGeometry = DummyTestClass::New();
aDummyGeometry->Initialize();
anotherDummyGeometry = aDummyGeometry->Clone();
aDummyGeometryOblique = DummyTestClass::New();
aDummyGeometryOblique->Initialize();
auto newBounds = aDummyGeometryOblique->GetBounds();
newBounds[0] = 0;
newBounds[1] = 5;
newBounds[2] = 10;
newBounds[3] = 20;
newBounds[4] = 30;
newBounds[5] = 40;
aDummyGeometryOblique->SetBounds(newBounds);
aDummyGeometryOblique->GetMatrixColumn(0);
auto obliqueTransform = mitk::AffineTransform3D::New();
mitk::AffineTransform3D::OutputVectorType rotationAxis(0.);
rotationAxis[1] = 1.;
obliqueTransform->Rotate3D(rotationAxis, 0.6);
mitk::AffineTransform3D::OutputVectorType translation;
translation[0] = 100.;
translation[1] = -50.;
translation[2] = -150.;
obliqueTransform->SetTranslation(translation);
aDummyGeometryOblique->SetIndexToWorldTransform(obliqueTransform);
}
void tearDown() override
{
aDummyGeometry = nullptr;
anotherDummyGeometry = nullptr;
}
// Test functions
void TestSetOrigin()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetOrigin(anotherPoint);
CPPUNIT_ASSERT(mitk::Equal(anotherPoint, dummy->GetOrigin()));
// undo changes, new and changed object need to be the same!
dummy->SetOrigin(aPoint);
DummyTestClass::Pointer newDummy = DummyTestClass::New();
MITK_ASSERT_EQUAL(dummy, newDummy, "TestSetOrigin");
}
void TestSetImageGeometry()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetImageGeometry(true);
CPPUNIT_ASSERT(dummy->GetImageGeometry());
// undo changes, new and changed object need to be the same!
dummy->SetImageGeometry(false);
CPPUNIT_ASSERT(dummy->GetImageGeometry() == false);
DummyTestClass::Pointer newDummy = DummyTestClass::New();
MITK_ASSERT_EQUAL(dummy, newDummy, "TestSetImageGeometry");
}
void TestSetFloatBounds()
{
float bounds[6] = {0, 11, 0, 12, 0, 13};
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetFloatBounds(bounds);
MITK_ASSERT_EQUAL(BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "BoundingBox equality");
// Wrong bounds, test needs to fail
bounds[1] = 7;
dummy->SetFloatBounds(bounds);
MITK_ASSERT_NOT_EQUAL(
BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "BoundingBox not equal");
// undo changes, new and changed object need to be the same!
float originalBounds[6] = {0, 1, 0, 1, 0, 1};
dummy->SetFloatBounds(originalBounds);
DummyTestClass::Pointer newDummy = DummyTestClass::New();
MITK_ASSERT_EQUAL(dummy, newDummy, "Undo and equal");
}
void TestSetBounds()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetBounds(anotherBoundingBox->GetBounds());
MITK_ASSERT_EQUAL(BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Setting bounds");
// Test needs to fail now
dummy->SetBounds(aThirdBoundingBox->GetBounds());
MITK_ASSERT_NOT_EQUAL(
BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Setting unequal bounds");
// undo changes, new and changed object need to be the same!
dummy->SetBounds(aBoundingBox->GetBounds());
DummyTestClass::Pointer newDummy = DummyTestClass::New();
MITK_ASSERT_EQUAL(dummy, newDummy, "Undo set bounds");
}
void TestSetFloatBoundsDouble()
{
double bounds[6] = {0, 11, 0, 12, 0, 13};
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetFloatBounds(bounds);
MITK_ASSERT_EQUAL(BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Float bounds");
// Test needs to fail now
bounds[3] = 7;
dummy->SetFloatBounds(bounds);
MITK_ASSERT_NOT_EQUAL(
BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Float bounds unequal");
// undo changes, new and changed object need to be the same!
double originalBounds[6] = {0, 1, 0, 1, 0, 1};
dummy->SetFloatBounds(originalBounds);
DummyTestClass::Pointer newDummy = DummyTestClass::New();
MITK_ASSERT_EQUAL(dummy, newDummy, "Undo set float bounds");
}
void TestSetFrameOfReferenceID()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetFrameOfReferenceID(5);
CPPUNIT_ASSERT(dummy->GetFrameOfReferenceID() == 5);
// undo changes, new and changed object need to be the same!
dummy->SetFrameOfReferenceID(0);
DummyTestClass::Pointer newDummy = DummyTestClass::New();
MITK_ASSERT_EQUAL(dummy, newDummy, "Undo set frame of reference");
}
void TestSetIndexToWorldTransform()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetIndexToWorldTransform(anotherTransform);
MITK_ASSERT_EQUAL(anotherTransform,
mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()),
"Compare IndexToWorldTransform 1");
// Test needs to fail now
dummy->SetIndexToWorldTransform(aThirdTransform);
MITK_ASSERT_NOT_EQUAL(anotherTransform,
mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()),
"Compare IndexToWorldTransform 2");
// undo changes, new and changed object need to be the same!
dummy->SetIndexToWorldTransform(aTransform);
DummyTestClass::Pointer newDummy = DummyTestClass::New();
MITK_ASSERT_EQUAL(dummy, newDummy, "Compare IndexToWorldTransform 3");
}
void TestSetIndexToWorldTransformWithoutChangingSpacing()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetIndexToWorldTransformWithoutChangingSpacing(anotherTransform);
CPPUNIT_ASSERT(mitk::Equal(aSpacing, dummy->GetSpacing(), mitk::eps, true));
// calculate a new version of anotherTransform, so that the spacing should be the same as the original spacing of
// aTransform.
mitk::AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix;
vnlmatrix = anotherTransform->GetMatrix().GetVnlMatrix();
mitk::VnlVector col;
- col = vnlmatrix.get_column(0);
+ col = vnlmatrix.get_column(0).as_ref();
col.normalize();
col *= aSpacing[0];
vnlmatrix.set_column(0, col);
- col = vnlmatrix.get_column(1);
+ col = vnlmatrix.get_column(1).as_ref();
col.normalize();
col *= aSpacing[1];
vnlmatrix.set_column(1, col);
- col = vnlmatrix.get_column(2);
+ col = vnlmatrix.get_column(2).as_ref();
col.normalize();
col *= aSpacing[2];
vnlmatrix.set_column(2, col);
mitk::Matrix3D matrix;
matrix = vnlmatrix;
anotherTransform->SetMatrix(matrix);
CPPUNIT_ASSERT(mitk::Equal(*anotherTransform, *(dummy->GetIndexToWorldTransform()), mitk::eps, true));
}
void TestSetIndexToWorldTransform_WithPointerToSameTransform()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetOrigin(anotherPoint);
dummy->SetIndexToWorldTransform(anotherTransform);
dummy->SetSpacing(anotherSpacing);
mitk::AffineTransform3D::Pointer testTransfrom = dummy->GetIndexToWorldTransform();
mitk::Vector3D modifiedPoint = anotherPoint.GetVectorFromOrigin() * 2.;
testTransfrom->SetOffset(modifiedPoint);
dummy->SetIndexToWorldTransform(testTransfrom);
CPPUNIT_ASSERT(mitk::Equal(modifiedPoint, dummy->GetOrigin().GetVectorFromOrigin()));
}
void TestSetIndexToWorldTransformByVtkMatrix()
{
vtkMatrix4x4 *vtkmatrix;
vtkmatrix = vtkMatrix4x4::New();
vtkmatrix->Identity();
vtkmatrix->SetElement(1, 1, 2);
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix);
MITK_ASSERT_EQUAL(anotherTransform,
mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()),
"Compare IndexToWorldTransformByVtkMatrix 1");
// test needs to fail now
vtkmatrix->SetElement(1, 1, 7);
dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix);
MITK_ASSERT_NOT_EQUAL(anotherTransform,
mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()),
"Compare IndexToWorldTransformByVtkMatrix 2");
// undo changes, new and changed object need to be the same!
vtkmatrix->SetElement(1, 1, 1);
dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix);
vtkmatrix->Delete();
DummyTestClass::Pointer newDummy = DummyTestClass::New();
MITK_ASSERT_EQUAL(dummy, newDummy, "Compare IndexToWorldTransformByVtkMatrix 3");
}
void TestSetIdentity()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
// Change IndextoWorldTransform and Origin
dummy->SetIndexToWorldTransform(anotherTransform);
dummy->SetOrigin(anotherPoint);
// Set Identity should reset ITWT and Origin
dummy->SetIdentity();
MITK_ASSERT_EQUAL(
aTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Test set identity 1");
CPPUNIT_ASSERT(mitk::Equal(aPoint, dummy->GetOrigin()));
CPPUNIT_ASSERT(mitk::Equal(aSpacing, dummy->GetSpacing()));
// new and changed object need to be the same!
DummyTestClass::Pointer newDummy = DummyTestClass::New();
MITK_ASSERT_EQUAL(dummy, newDummy, "Test set identity 2");
}
void TestSetSpacing()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetSpacing(anotherSpacing);
CPPUNIT_ASSERT(mitk::Equal(anotherSpacing, dummy->GetSpacing()));
// undo changes, new and changed object need to be the same!
dummy->SetSpacing(aSpacing);
DummyTestClass::Pointer newDummy = DummyTestClass::New();
MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy spacing");
}
void TestTransferItkToVtkTransform()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetIndexToWorldTransform(anotherTransform); // calls TransferItkToVtkTransform
mitk::AffineTransform3D::Pointer dummyTransform = dummy->GetIndexToWorldTransform();
CPPUNIT_ASSERT(mitk::MatrixEqualElementWise(anotherMatrix, dummyTransform->GetMatrix()));
}
void TestConstructors()
{
// test standard constructor
DummyTestClass::Pointer dummy1 = DummyTestClass::New();
bool test = dummy1->IsValid();
CPPUNIT_ASSERT(test == true);
CPPUNIT_ASSERT(dummy1->GetFrameOfReferenceID() == 0);
CPPUNIT_ASSERT(dummy1->GetIndexToWorldTransformLastModified() == 0);
CPPUNIT_ASSERT(mitk::Equal(dummy1->GetSpacing(), aSpacing));
CPPUNIT_ASSERT(mitk::Equal(dummy1->GetOrigin(), aPoint));
CPPUNIT_ASSERT(dummy1->GetImageGeometry() == false);
MITK_ASSERT_EQUAL(
mitk::AffineTransform3D::Pointer(dummy1->GetIndexToWorldTransform()), aTransform, "Contructor test 1");
MITK_ASSERT_EQUAL(
mitk::BaseGeometry::BoundingBoxType::ConstPointer(dummy1->GetBoundingBox()), aBoundingBox, "Constructor test 2");
DummyTestClass::Pointer dummy2 = DummyTestClass::New();
dummy2->SetOrigin(anotherPoint);
float bounds[6] = {0, 11, 0, 12, 0, 13};
dummy2->SetFloatBounds(bounds);
dummy2->SetIndexToWorldTransform(anotherTransform);
dummy2->SetSpacing(anotherSpacing);
DummyTestClass::Pointer dummy3 = DummyTestClass::New(*dummy2);
MITK_ASSERT_EQUAL(dummy3, dummy2, "Dummy contructor");
}
// Equal Tests
void Equal_CloneAndOriginal_ReturnsTrue() { MITK_ASSERT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Clone test"); }
void Equal_DifferentOrigin_ReturnsFalse()
{
anotherDummyGeometry->SetOrigin(anotherPoint);
MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different origin test");
}
void Equal_DifferentIndexToWorldTransform_ReturnsFalse()
{
anotherDummyGeometry->SetIndexToWorldTransform(anotherTransform);
MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different index to world");
}
void Equal_DifferentSpacing_ReturnsFalse()
{
anotherDummyGeometry->SetSpacing(anotherSpacing);
MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different spacing");
}
void Equal_InputIsNull_ReturnsFalse()
{
DummyTestClass::Pointer geometryNull = nullptr;
CPPUNIT_ASSERT_THROW(MITK_ASSERT_EQUAL(geometryNull, anotherDummyGeometry, "Input is null"), mitk::Exception);
}
void Equal_DifferentBoundingBox_ReturnsFalse()
{
// create different bounds to make the comparison false
mitk::ScalarType bounds[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
anotherDummyGeometry->SetBounds(bounds);
MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different bounding box");
}
void Equal_Transforms_MinorDifferences_And_Eps()
{
// Verifies that the eps parameter is evaluated properly
// when comparing two mitk::BaseGeometry::TransformTypes
aMatrix.SetIdentity();
anotherMatrix.SetIdentity();
aMatrix(0, 1) = 0.0002;
aTransform->SetMatrix(aMatrix);
anotherMatrix(0, 1) = 0.0002;
anotherTransform->SetMatrix(anotherMatrix);
anotherTransform->SetMatrix(aMatrix);
CPPUNIT_ASSERT_MESSAGE("Exact same transforms are mitk::Equal() for eps=mitk::eps",
mitk::Equal(*aTransform, *anotherTransform, mitk::eps, true));
CPPUNIT_ASSERT_MESSAGE("Exact same transforms are mitk::Equal() for eps=vnl_math::eps",
mitk::Equal(*aTransform, *anotherTransform, vnl_math::eps, true));
anotherMatrix(0, 1) = 0.0002 + mitk::eps;
anotherTransform->SetMatrix(anotherMatrix);
CPPUNIT_ASSERT_MESSAGE("Transforms of diff mitk::eps are !mitk::Equal() for eps=vnl_math::eps",
!mitk::Equal(*aTransform, *anotherTransform, vnl_math::eps, true));
CPPUNIT_ASSERT_MESSAGE("Transforms of diff mitk::eps are !mitk::Equal() for eps=mitk::eps-1%",
!mitk::Equal(*aTransform, *anotherTransform, mitk::eps * 0.99, true));
CPPUNIT_ASSERT_MESSAGE("Transforms of diff mitk::eps _are_ mitk::Equal() for eps=mitk::eps+1%",
mitk::Equal(*aTransform, *anotherTransform, mitk::eps * 1.01, true));
}
void TestComposeTransform()
{
// Create Transformations to set and compare
mitk::AffineTransform3D::Pointer transform1;
transform1 = mitk::AffineTransform3D::New();
mitk::AffineTransform3D::MatrixType matrix1;
matrix1.SetIdentity();
matrix1(1, 1) = 2;
transform1->SetMatrix(matrix1); // Spacing = 2
mitk::AffineTransform3D::Pointer transform2;
transform2 = mitk::AffineTransform3D::New();
mitk::AffineTransform3D::MatrixType matrix2;
matrix2.SetIdentity();
matrix2(1, 1) = 2;
transform2->SetMatrix(matrix2); // Spacing = 2
mitk::AffineTransform3D::Pointer transform3;
transform3 = mitk::AffineTransform3D::New();
mitk::AffineTransform3D::MatrixType matrix3;
matrix3.SetIdentity();
matrix3(1, 1) = 4;
transform3->SetMatrix(matrix3); // Spacing = 4
mitk::AffineTransform3D::Pointer transform4;
transform4 = mitk::AffineTransform3D::New();
mitk::AffineTransform3D::MatrixType matrix4;
matrix4.SetIdentity();
matrix4(1, 1) = 0.25;
transform4->SetMatrix(matrix4); // Spacing = 0.25
// Vector to compare spacing
mitk::Vector3D expectedSpacing;
expectedSpacing.Fill(1.0);
expectedSpacing[1] = 4;
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetIndexToWorldTransform(transform1); // Spacing = 2
dummy->Compose(transform2); // Spacing = 4
CPPUNIT_ASSERT(mitk::Equal(dummy->GetSpacing(), expectedSpacing));
MITK_ASSERT_EQUAL(
transform3, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compose transform 2"); // 4=4
// undo changes, new and changed object need to be the same!
dummy->Compose(transform4); // Spacing = 1
DummyTestClass::Pointer newDummy = DummyTestClass::New();
MITK_ASSERT_EQUAL(dummy, newDummy, "Compose transform 3"); // 1=1
}
void TestComposeVtkMatrix()
{
// Create Transformations to set and compare
mitk::AffineTransform3D::Pointer transform1;
transform1 = mitk::AffineTransform3D::New();
mitk::AffineTransform3D::MatrixType matrix1;
matrix1.SetIdentity();
matrix1(1, 1) = 2;
transform1->SetMatrix(matrix1); // Spacing = 2
vtkMatrix4x4 *vtkmatrix2;
vtkmatrix2 = vtkMatrix4x4::New();
vtkmatrix2->Identity();
vtkmatrix2->SetElement(1, 1, 2); // Spacing = 2
mitk::AffineTransform3D::Pointer transform3;
transform3 = mitk::AffineTransform3D::New();
mitk::AffineTransform3D::MatrixType matrix3;
matrix3.SetIdentity();
matrix3(1, 1) = 4;
transform3->SetMatrix(matrix3); // Spacing = 4
vtkMatrix4x4 *vtkmatrix4;
vtkmatrix4 = vtkMatrix4x4::New();
vtkmatrix4->Identity();
vtkmatrix4->SetElement(1, 1, 0.25); // Spacing = 0.25
// Vector to compare spacing
mitk::Vector3D expectedSpacing;
expectedSpacing.Fill(1.0);
expectedSpacing[1] = 4;
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetIndexToWorldTransform(transform1); // Spacing = 2
dummy->Compose(vtkmatrix2); // Spacing = 4
vtkmatrix2->Delete();
MITK_ASSERT_EQUAL(
transform3, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compose vtk matrix"); // 4=4
CPPUNIT_ASSERT(mitk::Equal(dummy->GetSpacing(), expectedSpacing));
// undo changes, new and changed object need to be the same!
dummy->Compose(vtkmatrix4); // Spacing = 1
vtkmatrix4->Delete();
DummyTestClass::Pointer newDummy = DummyTestClass::New();
MITK_ASSERT_EQUAL(dummy, newDummy, "Compose vtk"); // 1=1
}
void TestTranslate()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetOrigin(anotherPoint);
CPPUNIT_ASSERT(mitk::Equal(anotherPoint, dummy->GetOrigin()));
// use some random values for translation
mitk::Vector3D translationVector;
translationVector.SetElement(0, 17.5f);
translationVector.SetElement(1, -32.3f);
translationVector.SetElement(2, 4.0f);
// compute ground truth
mitk::Point3D tmpResult = anotherPoint + translationVector;
dummy->Translate(translationVector);
CPPUNIT_ASSERT(mitk::Equal(dummy->GetOrigin(), tmpResult));
// undo changes
translationVector *= -1;
dummy->Translate(translationVector);
CPPUNIT_ASSERT(mitk::Equal(dummy->GetOrigin(), anotherPoint));
// undo changes, new and changed object need to be the same!
translationVector.SetElement(0, -1 * anotherPoint[0]);
translationVector.SetElement(1, -1 * anotherPoint[1]);
translationVector.SetElement(2, -1 * anotherPoint[2]);
dummy->Translate(translationVector);
DummyTestClass::Pointer newDummy = DummyTestClass::New();
MITK_ASSERT_EQUAL(dummy, newDummy, "Translate test");
}
// a part of the test requires axis-parallel coordinates
int testIndexAndWorldConsistency(DummyTestClass::Pointer dummyGeometry)
{
// Testing consistency of index and world coordinate systems
mitk::Point3D origin = dummyGeometry->GetOrigin();
mitk::Point3D dummyPoint;
// Testing index->world->index conversion consistency
dummyGeometry->WorldToIndex(origin, dummyPoint);
dummyGeometry->IndexToWorld(dummyPoint, dummyPoint);
CPPUNIT_ASSERT(mitk::EqualArray(dummyPoint, origin, 3, mitk::eps, true));
// Testing WorldToIndex(origin, mitk::Point3D)==(0,0,0)
mitk::Point3D globalOrigin;
mitk::FillVector3D(globalOrigin, 0, 0, 0);
mitk::Point3D originContinuousIndex;
dummyGeometry->WorldToIndex(origin, originContinuousIndex);
CPPUNIT_ASSERT(mitk::EqualArray(originContinuousIndex, globalOrigin, 3, mitk::eps, true));
// Testing WorldToIndex(origin, itk::Index)==(0,0,0)
itk::Index<3> itkindex;
dummyGeometry->WorldToIndex(origin, itkindex);
itk::Index<3> globalOriginIndex;
mitk::vtk2itk(globalOrigin, globalOriginIndex);
CPPUNIT_ASSERT(mitk::EqualArray(itkindex, globalOriginIndex, 3, mitk::eps, true));
// Testing WorldToIndex(origin-0.5*spacing, itk::Index)==(0,0,0)
mitk::Vector3D halfSpacingStep = dummyGeometry->GetSpacing() * 0.5;
mitk::Matrix3D rotation;
mitk::Point3D originOffCenter = origin - halfSpacingStep;
dummyGeometry->WorldToIndex(originOffCenter, itkindex);
CPPUNIT_ASSERT(mitk::EqualArray(itkindex, globalOriginIndex, 3, mitk::eps, true));
// Testing WorldToIndex(origin+0.5*spacing-eps, itk::Index)==(0,0,0)
originOffCenter = origin + halfSpacingStep;
originOffCenter -= 0.0001;
dummyGeometry->WorldToIndex(originOffCenter, itkindex);
CPPUNIT_ASSERT(mitk::EqualArray(itkindex, globalOriginIndex, 3, mitk::eps, true));
// Testing WorldToIndex(origin+0.5*spacing, itk::Index)==(1,1,1)");
originOffCenter = origin + halfSpacingStep;
itk::Index<3> global111;
mitk::FillVector3D(global111, 1, 1, 1);
dummyGeometry->WorldToIndex(originOffCenter, itkindex);
CPPUNIT_ASSERT(mitk::EqualArray(itkindex, global111, 3, mitk::eps, true));
// Testing WorldToIndex(GetCenter())==BoundingBox.GetCenter
mitk::Point3D center = dummyGeometry->GetCenter();
mitk::Point3D centerContIndex;
dummyGeometry->WorldToIndex(center, centerContIndex);
mitk::BoundingBox::ConstPointer boundingBox = dummyGeometry->GetBoundingBox();
mitk::BoundingBox::PointType centerBounds = boundingBox->GetCenter();
CPPUNIT_ASSERT(mitk::Equal(centerContIndex, centerBounds));
// Testing GetCenter()==IndexToWorld(BoundingBox.GetCenter)
center = dummyGeometry->GetCenter();
mitk::Point3D centerBoundsInWorldCoords;
dummyGeometry->IndexToWorld(centerBounds, centerBoundsInWorldCoords);
CPPUNIT_ASSERT(mitk::Equal(center, centerBoundsInWorldCoords));
// Test using random point,
// Testing consistency of index and world coordinate systems
mitk::Point3D point;
mitk::FillVector3D(point, 3.5, -2, 4.6);
// Testing index->world->index conversion consistency
dummyGeometry->WorldToIndex(point, dummyPoint);
dummyGeometry->IndexToWorld(dummyPoint, dummyPoint);
CPPUNIT_ASSERT(mitk::EqualArray(dummyPoint, point, 3, mitk::eps, true));
return EXIT_SUCCESS;
}
int testIndexAndWorldConsistencyForVectors(DummyTestClass::Pointer dummyGeometry)
{
// Testing consistency of index and world coordinate systems for vectors
mitk::Vector3D xAxisMM = dummyGeometry->GetAxisVector(0);
mitk::Vector3D xAxisContinuousIndex;
mitk::Point3D p, pIndex, origin;
origin = dummyGeometry->GetOrigin();
p[0] = xAxisMM[0] + origin[0];
p[1] = xAxisMM[1] + origin[1];
p[2] = xAxisMM[2] + origin[2];
dummyGeometry->WorldToIndex(p, pIndex);
dummyGeometry->WorldToIndex(xAxisMM, xAxisContinuousIndex);
CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[0], pIndex[0]));
CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[1], pIndex[1]));
CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[2], pIndex[2]));
dummyGeometry->IndexToWorld(xAxisContinuousIndex, xAxisContinuousIndex);
dummyGeometry->IndexToWorld(pIndex, p);
CPPUNIT_ASSERT(xAxisContinuousIndex == xAxisMM);
CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[0], p[0] - origin[0]));
CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[1], p[1] - origin[1]));
CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[2], p[2] - origin[2]));
// Test consictency for random vector
mitk::Vector3D vector;
mitk::FillVector3D(vector, 2.5, -3.2, 8.1);
mitk::Vector3D vectorContinuousIndex;
p[0] = vector[0] + origin[0];
p[1] = vector[1] + origin[1];
p[2] = vector[2] + origin[2];
dummyGeometry->WorldToIndex(p, pIndex);
dummyGeometry->WorldToIndex(vector, vectorContinuousIndex);
CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[0], pIndex[0]));
CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[1], pIndex[1]));
CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[2], pIndex[2]));
dummyGeometry->IndexToWorld(vectorContinuousIndex, vectorContinuousIndex);
dummyGeometry->IndexToWorld(pIndex, p);
CPPUNIT_ASSERT(vectorContinuousIndex == vector);
CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[0], p[0] - origin[0]));
CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[1], p[1] - origin[1]));
CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[2], p[2] - origin[2]));
return EXIT_SUCCESS;
}
int testIndexAndWorldConsistencyForIndex(DummyTestClass::Pointer dummyGeometry)
{
// Testing consistency of index and world coordinate systems
// creating testing data
itk::Index<4> itkIndex4, itkIndex4b;
itk::Index<3> itkIndex3, itkIndex3b;
itk::Index<2> itkIndex2, itkIndex2b;
itk::Index<3> mitkIndex, mitkIndexb;
itkIndex4[0] = itkIndex4[1] = itkIndex4[2] = itkIndex4[3] = 4;
itkIndex3[0] = itkIndex3[1] = itkIndex3[2] = 6;
itkIndex2[0] = itkIndex2[1] = 2;
mitkIndex[0] = mitkIndex[1] = mitkIndex[2] = 13;
// check for constistency
mitk::Point3D point;
dummyGeometry->IndexToWorld(itkIndex2, point);
dummyGeometry->WorldToIndex(point, itkIndex2b);
CPPUNIT_ASSERT(((itkIndex2b[0] == itkIndex2[0]) && (itkIndex2b[1] == itkIndex2[1])));
// Testing itk::index<2> for IndexToWorld/WorldToIndex consistency
dummyGeometry->IndexToWorld(itkIndex3, point);
dummyGeometry->WorldToIndex(point, itkIndex3b);
CPPUNIT_ASSERT(
((itkIndex3b[0] == itkIndex3[0]) && (itkIndex3b[1] == itkIndex3[1]) && (itkIndex3b[2] == itkIndex3[2])));
// Testing itk::index<3> for IndexToWorld/WorldToIndex consistency
dummyGeometry->IndexToWorld(itkIndex4, point);
dummyGeometry->WorldToIndex(point, itkIndex4b);
CPPUNIT_ASSERT(((itkIndex4b[0] == itkIndex4[0]) && (itkIndex4b[1] == itkIndex4[1]) &&
(itkIndex4b[2] == itkIndex4[2]) && (itkIndex4b[3] == 0)));
// Testing itk::index<3> for IndexToWorld/WorldToIndex consistency
dummyGeometry->IndexToWorld(mitkIndex, point);
dummyGeometry->WorldToIndex(point, mitkIndexb);
CPPUNIT_ASSERT(
((mitkIndexb[0] == mitkIndex[0]) && (mitkIndexb[1] == mitkIndex[1]) && (mitkIndexb[2] == mitkIndex[2])));
// Testing mitk::Index for IndexToWorld/WorldToIndex consistency
return EXIT_SUCCESS;
}
void TestIndexToWorld()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
testIndexAndWorldConsistency(dummy);
testIndexAndWorldConsistencyForVectors(dummy);
testIndexAndWorldConsistencyForIndex(dummy);
// Geometry must not have changed
DummyTestClass::Pointer newDummy = DummyTestClass::New();
MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy index to world");
// Test with other geometries
dummy->SetOrigin(anotherPoint);
testIndexAndWorldConsistency(dummy);
testIndexAndWorldConsistencyForVectors(dummy);
testIndexAndWorldConsistencyForIndex(dummy);
dummy->SetIndexToWorldTransform(anotherTransform);
testIndexAndWorldConsistency(dummy);
testIndexAndWorldConsistencyForVectors(dummy);
testIndexAndWorldConsistencyForIndex(dummy);
dummy->SetOrigin(anotherPoint);
testIndexAndWorldConsistency(dummy);
testIndexAndWorldConsistencyForVectors(dummy);
testIndexAndWorldConsistencyForIndex(dummy);
dummy->SetSpacing(anotherSpacing);
testIndexAndWorldConsistency(dummy);
testIndexAndWorldConsistencyForVectors(dummy);
testIndexAndWorldConsistencyForIndex(dummy);
}
void TestExecuteOperation()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
// Do same Operations with new Dummy and compare
DummyTestClass::Pointer newDummy = DummyTestClass::New();
// Test operation Nothing
auto opN = new mitk::Operation(mitk::OpNOTHING);
dummy->ExecuteOperation(opN);
MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy execute operation 1");
// Test operation Move
auto opP = new mitk::PointOperation(mitk::OpMOVE, anotherPoint);
dummy->ExecuteOperation(opP);
CPPUNIT_ASSERT(mitk::Equal(anotherPoint, dummy->GetOrigin()));
newDummy->SetOrigin(anotherPoint);
MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy execute operation 2");
// Test operation Scale, Scale sets spacing to scale+1
mitk::Point3D spacing;
spacing[0] = anotherSpacing[0] - 1.;
spacing[1] = anotherSpacing[1] - 1.;
spacing[2] = anotherSpacing[2] - 1.;
auto opS = new mitk::ScaleOperation(mitk::OpSCALE, spacing, anotherPoint);
dummy->ExecuteOperation(opS);
CPPUNIT_ASSERT(mitk::Equal(anotherSpacing, dummy->GetSpacing()));
newDummy->SetSpacing(anotherSpacing);
MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy execute operation 3");
// change Geometry to test more cases
dummy->SetIndexToWorldTransform(anotherTransform);
dummy->SetSpacing(anotherSpacing);
// Testing a rotation of the geometry
double angle = 35.0;
mitk::Vector3D rotationVector;
mitk::FillVector3D(rotationVector, 1, 0, 0);
mitk::Point3D center = dummy->GetCenter();
auto opR = new mitk::RotationOperation(mitk::OpROTATE, center, rotationVector, angle);
dummy->ExecuteOperation(opR);
mitk::Matrix3D rotation;
mitk::GetRotation(dummy, rotation);
mitk::Vector3D voxelStep = rotation * anotherSpacing;
mitk::Vector3D voxelStepIndex;
dummy->WorldToIndex(voxelStep, voxelStepIndex);
mitk::Vector3D expectedVoxelStepIndex;
expectedVoxelStepIndex.Fill(1);
CPPUNIT_ASSERT(mitk::Equal(voxelStepIndex, expectedVoxelStepIndex));
delete opR;
delete opN;
delete opS;
delete opP;
}
void TestCalculateBoundingBoxRelToTransform()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetExtentInMM(0, 15);
dummy->SetExtentInMM(1, 20);
dummy->SetExtentInMM(2, 8);
mitk::BoundingBox::Pointer dummyBoundingBox = dummy->CalculateBoundingBoxRelativeToTransform(anotherTransform);
mitk::BoundingBox::PointsContainer::Pointer pointscontainer = mitk::BoundingBox::PointsContainer::New();
mitk::BoundingBox::PointIdentifier pointid = 0;
unsigned char i;
mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New();
anotherTransform->GetInverse(inverse);
for (i = 0; i < 8; ++i)
pointscontainer->InsertElement(pointid++, inverse->TransformPoint(dummy->GetCornerPoint(i)));
mitk::BoundingBox::Pointer result = mitk::BoundingBox::New();
result->SetPoints(pointscontainer);
result->ComputeBoundingBox();
MITK_ASSERT_EQUAL(result, dummyBoundingBox, "BBox rel to transform");
// dummy still needs to be unchanged, except for extend
DummyTestClass::Pointer newDummy = DummyTestClass::New();
newDummy->SetExtentInMM(0, 15);
newDummy->SetExtentInMM(1, 20);
newDummy->SetExtentInMM(2, 8);
MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy BBox");
}
// void TestSetTimeBounds(){
// mitk::TimeBounds timeBounds;
// timeBounds[0] = 1;
// timeBounds[1] = 9;
// DummyTestClass::Pointer dummy = DummyTestClass::New();
// dummy->SetTimeBounds(timeBounds);
// mitk::TimeBounds timeBounds2 = dummy->GetTimeBounds();
// CPPUNIT_ASSERT(timeBounds[0]==timeBounds2[0]);
// CPPUNIT_ASSERT(timeBounds[1]==timeBounds2[1]);
// //undo changes, new and changed object need to be the same!
// timeBounds[0]=mitk::ScalarTypeNumericTraits::NonpositiveMin();
// timeBounds[1]=mitk::ScalarTypeNumericTraits::max();
// DummyTestClass::Pointer newDummy = DummyTestClass::New();
// CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true));
//}
void TestIs2DConvertable()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
// new initialized geometry is 2D convertable
CPPUNIT_ASSERT(dummy->Is2DConvertable());
// Wrong Spacing needs to fail
dummy->SetSpacing(anotherSpacing);
CPPUNIT_ASSERT(dummy->Is2DConvertable() == false);
// undo
dummy->SetSpacing(aSpacing);
CPPUNIT_ASSERT(dummy->Is2DConvertable());
// Wrong Origin needs to fail
dummy->SetOrigin(anotherPoint);
CPPUNIT_ASSERT(dummy->Is2DConvertable() == false);
// undo
dummy->SetOrigin(aPoint);
CPPUNIT_ASSERT(dummy->Is2DConvertable());
// third dimension must not be transformed
mitk::AffineTransform3D::Pointer dummyTransform = mitk::AffineTransform3D::New();
mitk::AffineTransform3D::MatrixType dummyMatrix;
dummyMatrix.SetIdentity();
dummyTransform->SetMatrix(dummyMatrix);
dummy->SetIndexToWorldTransform(dummyTransform);
// identity matrix is 2DConvertable
CPPUNIT_ASSERT(dummy->Is2DConvertable());
dummyMatrix(0, 2) = 3;
dummyTransform->SetMatrix(dummyMatrix);
CPPUNIT_ASSERT(dummy->Is2DConvertable() == false);
dummyMatrix.SetIdentity();
dummyMatrix(1, 2) = 0.4;
dummyTransform->SetMatrix(dummyMatrix);
CPPUNIT_ASSERT(dummy->Is2DConvertable() == false);
dummyMatrix.SetIdentity();
dummyMatrix(2, 2) = 3;
dummyTransform->SetMatrix(dummyMatrix);
CPPUNIT_ASSERT(dummy->Is2DConvertable() == false);
dummyMatrix.SetIdentity();
dummyMatrix(2, 1) = 3;
dummyTransform->SetMatrix(dummyMatrix);
CPPUNIT_ASSERT(dummy->Is2DConvertable() == false);
dummyMatrix.SetIdentity();
dummyMatrix(2, 0) = 3;
dummyTransform->SetMatrix(dummyMatrix);
CPPUNIT_ASSERT(dummy->Is2DConvertable() == false);
// undo changes, new and changed object need to be the same!
dummyMatrix.SetIdentity();
dummyTransform->SetMatrix(dummyMatrix);
DummyTestClass::Pointer newDummy = DummyTestClass::New();
MITK_ASSERT_EQUAL(dummy, newDummy, "Is 2D convertable");
}
void TestGetCornerPoint()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetIndexToWorldTransform(anotherTransform);
double bounds[6] = {0, 11, 0, 12, 0, 13};
dummy->SetFloatBounds(bounds);
mitk::Point3D corner, refCorner;
// Corner 0
mitk::FillVector3D(refCorner, bounds[0], bounds[2], bounds[4]);
refCorner = anotherTransform->TransformPoint(refCorner);
corner = dummy->GetCornerPoint(0);
CPPUNIT_ASSERT(mitk::Equal(refCorner, corner));
corner = dummy->GetCornerPoint(true, true, true);
CPPUNIT_ASSERT(mitk::Equal(refCorner, corner));
// Corner 1
mitk::FillVector3D(refCorner, bounds[0], bounds[2], bounds[5]);
refCorner = anotherTransform->TransformPoint(refCorner);
corner = dummy->GetCornerPoint(1);
CPPUNIT_ASSERT(mitk::Equal(refCorner, corner));
corner = dummy->GetCornerPoint(true, true, false);
CPPUNIT_ASSERT(mitk::Equal(refCorner, corner));
// Corner 2
mitk::FillVector3D(refCorner, bounds[0], bounds[3], bounds[4]);
refCorner = anotherTransform->TransformPoint(refCorner);
corner = dummy->GetCornerPoint(2);
CPPUNIT_ASSERT(mitk::Equal(refCorner, corner));
corner = dummy->GetCornerPoint(true, false, true);
CPPUNIT_ASSERT(mitk::Equal(refCorner, corner));
// Corner 3
mitk::FillVector3D(refCorner, bounds[0], bounds[3], bounds[5]);
refCorner = anotherTransform->TransformPoint(refCorner);
corner = dummy->GetCornerPoint(3);
CPPUNIT_ASSERT(mitk::Equal(refCorner, corner));
corner = dummy->GetCornerPoint(true, false, false);
CPPUNIT_ASSERT(mitk::Equal(refCorner, corner));
// Corner 4
mitk::FillVector3D(refCorner, bounds[1], bounds[2], bounds[4]);
refCorner = anotherTransform->TransformPoint(refCorner);
corner = dummy->GetCornerPoint(4);
CPPUNIT_ASSERT(mitk::Equal(refCorner, corner));
corner = dummy->GetCornerPoint(false, true, true);
CPPUNIT_ASSERT(mitk::Equal(refCorner, corner));
// Corner 5
mitk::FillVector3D(refCorner, bounds[1], bounds[2], bounds[5]);
refCorner = anotherTransform->TransformPoint(refCorner);
corner = dummy->GetCornerPoint(5);
CPPUNIT_ASSERT(mitk::Equal(refCorner, corner));
corner = dummy->GetCornerPoint(false, true, false);
CPPUNIT_ASSERT(mitk::Equal(refCorner, corner));
// Corner 6
mitk::FillVector3D(refCorner, bounds[1], bounds[3], bounds[4]);
refCorner = anotherTransform->TransformPoint(refCorner);
corner = dummy->GetCornerPoint(6);
CPPUNIT_ASSERT(mitk::Equal(refCorner, corner));
corner = dummy->GetCornerPoint(false, false, true);
CPPUNIT_ASSERT(mitk::Equal(refCorner, corner));
// Corner 7
mitk::FillVector3D(refCorner, bounds[1], bounds[3], bounds[5]);
refCorner = anotherTransform->TransformPoint(refCorner);
corner = dummy->GetCornerPoint(7);
CPPUNIT_ASSERT(mitk::Equal(refCorner, corner));
corner = dummy->GetCornerPoint(false, false, false);
CPPUNIT_ASSERT(mitk::Equal(refCorner, corner));
// Wrong Corner needs to fail
CPPUNIT_ASSERT_THROW(dummy->GetCornerPoint(20), itk::ExceptionObject);
// dummy geometry must not have changed!
DummyTestClass::Pointer newDummy = DummyTestClass::New();
newDummy->SetIndexToWorldTransform(anotherTransform);
newDummy->SetFloatBounds(bounds);
MITK_ASSERT_EQUAL(dummy, newDummy, "Corner point");
}
void TestExtentInMM()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetExtentInMM(0, 50);
CPPUNIT_ASSERT(mitk::Equal(50., dummy->GetExtentInMM(0)));
// Vnl Matrix has changed. The next line only works because the spacing is 1!
CPPUNIT_ASSERT(
mitk::Equal(50., dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).magnitude()));
// Smaller extent than original
dummy->SetExtentInMM(0, 5);
CPPUNIT_ASSERT(mitk::Equal(5., dummy->GetExtentInMM(0)));
CPPUNIT_ASSERT(
mitk::Equal(5., dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).magnitude()));
dummy->SetExtentInMM(1, 4);
CPPUNIT_ASSERT(mitk::Equal(4., dummy->GetExtentInMM(1)));
CPPUNIT_ASSERT(
mitk::Equal(4., dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).magnitude()));
dummy->SetExtentInMM(2, 2.5);
CPPUNIT_ASSERT(mitk::Equal(2.5, dummy->GetExtentInMM(2)));
CPPUNIT_ASSERT(
mitk::Equal(2.5, dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).magnitude()));
}
void TestGetAxisVector()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetIndexToWorldTransform(anotherTransform);
double bounds[6] = {0, 11, 0, 12, 0, 13};
dummy->SetFloatBounds(bounds);
mitk::Vector3D vector;
mitk::FillVector3D(vector, bounds[1], 0, 0);
dummy->IndexToWorld(vector, vector);
CPPUNIT_ASSERT(mitk::Equal(dummy->GetAxisVector(0), vector));
mitk::FillVector3D(vector, 0, bounds[3], 0);
dummy->IndexToWorld(vector, vector);
CPPUNIT_ASSERT(mitk::Equal(dummy->GetAxisVector(1), vector));
mitk::FillVector3D(vector, 0, 0, bounds[5]);
dummy->IndexToWorld(vector, vector);
CPPUNIT_ASSERT(mitk::Equal(dummy->GetAxisVector(2), vector));
}
void TestGetCenter()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetIndexToWorldTransform(anotherTransform);
double bounds[6] = {0, 11, 2, 12, 1, 13};
dummy->SetFloatBounds(bounds);
mitk::Point3D refCenter;
for (int i = 0; i < 3; i++)
refCenter.SetElement(i, (bounds[2 * i] + bounds[2 * i + 1]) / 2.0);
dummy->IndexToWorld(refCenter, refCenter);
CPPUNIT_ASSERT(mitk::Equal(dummy->GetCenter(), refCenter));
}
void TestGetDiagonalLength()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
double bounds[6] = {1, 3, 5, 8, 7.5, 11.5};
dummy->SetFloatBounds(bounds);
// 3-1=2, 8-5=3, 11.5-7.5=4; 2^2+3^2+4^2 = 29
double expectedLength = sqrt(29.);
CPPUNIT_ASSERT(mitk::Equal(expectedLength, dummy->GetDiagonalLength(), mitk::eps, true));
CPPUNIT_ASSERT(mitk::Equal(29., dummy->GetDiagonalLength2(), mitk::eps, true));
// dummy must not have changed
DummyTestClass::Pointer newDummy = DummyTestClass::New();
newDummy->SetFloatBounds(bounds);
MITK_ASSERT_EQUAL(dummy, newDummy, "Diagonal length");
}
void TestGetExtent()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
double bounds[6] = {1, 3, 5, 8, 7.5, 11.5};
dummy->SetFloatBounds(bounds);
CPPUNIT_ASSERT(mitk::Equal(2., dummy->GetExtent(0)));
CPPUNIT_ASSERT(mitk::Equal(3., dummy->GetExtent(1)));
CPPUNIT_ASSERT(mitk::Equal(4., dummy->GetExtent(2)));
// dummy must not have changed
DummyTestClass::Pointer newDummy = DummyTestClass::New();
newDummy->SetFloatBounds(bounds);
MITK_ASSERT_EQUAL(dummy, newDummy, "Extend");
}
void TestIsInside()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
double bounds[6] = {1, 3, 5, 8, 7.5, 11.5};
dummy->SetFloatBounds(bounds);
mitk::Point3D insidePoint;
mitk::Point3D outsidePoint;
mitk::FillVector3D(insidePoint, 2, 6, 7.6);
mitk::FillVector3D(outsidePoint, 0, 9, 8.2);
CPPUNIT_ASSERT(dummy->IsIndexInside(insidePoint));
CPPUNIT_ASSERT(false == dummy->IsIndexInside(outsidePoint));
dummy->IndexToWorld(insidePoint, insidePoint);
dummy->IndexToWorld(outsidePoint, outsidePoint);
CPPUNIT_ASSERT(dummy->IsInside(insidePoint));
CPPUNIT_ASSERT(false == dummy->IsInside(outsidePoint));
// dummy must not have changed
DummyTestClass::Pointer newDummy = DummyTestClass::New();
newDummy->SetFloatBounds(bounds);
MITK_ASSERT_EQUAL(dummy, newDummy, "Is inside");
}
void TestInitialize()
{
// test standard constructor
DummyTestClass::Pointer dummy1 = DummyTestClass::New();
DummyTestClass::Pointer dummy2 = DummyTestClass::New();
dummy2->SetOrigin(anotherPoint);
dummy2->SetBounds(anotherBoundingBox->GetBounds());
// mitk::TimeBounds timeBounds;
// timeBounds[0] = 1;
// timeBounds[1] = 9;
// dummy2->SetTimeBounds(timeBounds);
dummy2->SetIndexToWorldTransform(anotherTransform);
dummy2->SetSpacing(anotherSpacing);
dummy1->InitializeGeometry(dummy2);
MITK_ASSERT_EQUAL(dummy1, dummy2, "Initialize 1");
dummy1->Initialize();
DummyTestClass::Pointer dummy3 = DummyTestClass::New();
MITK_ASSERT_EQUAL(dummy3, dummy1, "Initialize 2");
}
void TestGetMatrixColumn()
{
DummyTestClass::Pointer dummy = DummyTestClass::New();
dummy->SetIndexToWorldTransform(anotherTransform);
mitk::Vector3D testVector, refVector;
testVector.SetVnlVector(dummy->GetMatrixColumn(0));
mitk::FillVector3D(refVector, 1, 0, 0);
CPPUNIT_ASSERT(testVector == refVector);
testVector.SetVnlVector(dummy->GetMatrixColumn(1));
mitk::FillVector3D(refVector, 0, 2, 0);
CPPUNIT_ASSERT(testVector == refVector);
testVector.SetVnlVector(dummy->GetMatrixColumn(2));
mitk::FillVector3D(refVector, 0, 0, 1);
CPPUNIT_ASSERT(testVector == refVector);
// dummy must not have changed
DummyTestClass::Pointer newDummy = DummyTestClass::New();
newDummy->SetIndexToWorldTransform(anotherTransform);
MITK_ASSERT_EQUAL(dummy, newDummy, "GetMatrixColumn");
}
void IsSubGeometry_Spacing()
{
CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometry, *aDummyGeometry, mitk::eps, true));
for (unsigned int i = 0; i < 3; ++i)
{
mitk::Vector3D wrongSpacing = aDummyGeometry->GetSpacing();
wrongSpacing[i] += mitk::eps * 2;
auto wrongGeometry = aDummyGeometry->Clone();
wrongGeometry->SetSpacing(wrongSpacing);
CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true));
}
for (unsigned int i = 0; i < 3; ++i)
{
mitk::Vector3D wrongSpacing = aDummyGeometry->GetSpacing();
wrongSpacing[i] -= mitk::eps * 2;
auto wrongGeometry = aDummyGeometry->Clone();
wrongGeometry->SetSpacing(wrongSpacing);
CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true));
}
}
void IsSubGeometry_TransformMatrix()
{
CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometry, *aDummyGeometry, mitk::eps, true));
for (unsigned int i = 0; i < 3; ++i)
{
for (unsigned int j = 0; j < 3; ++j)
{
itk::Matrix<mitk::ScalarType, 3, 3> wrongMatrix = aDummyGeometry->GetIndexToWorldTransform()->GetMatrix();
wrongMatrix[i][j] += mitk::eps * 2;
auto wrongGeometry = aDummyGeometry->Clone();
wrongGeometry->GetIndexToWorldTransform()->SetMatrix(wrongMatrix);
CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true));
}
}
}
void IsSubGeometry_Bounds_NoneImage()
{
IsSubGeometry_Bounds_internal(false);
}
void IsSubGeometry_Bounds_Image()
{
IsSubGeometry_Bounds_internal(true);
}
void IsSubGeometry_Bounds_internal(bool isImage)
{
auto newBounds = aDummyGeometry->GetBounds();
newBounds[0] = 10;
newBounds[1] = 20;
newBounds[2] = 10;
newBounds[3] = 20;
newBounds[4] = 10;
newBounds[5] = 20;
aDummyGeometry->SetBounds(newBounds);
aDummyGeometry->SetImageGeometry(isImage);
CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometry, *aDummyGeometry, mitk::eps, true));
for (unsigned int i = 0; i < 6; ++i)
{
auto legalBounds = newBounds;
if (i % 2 == 0)
{
legalBounds[i] += 1;
}
else
{
legalBounds[i] -= 1;
}
auto legalGeometry = aDummyGeometry->Clone();
legalGeometry->SetBounds(legalBounds);
CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometry, mitk::eps, true));
}
for (unsigned int i = 0; i < 6; ++i)
{
auto wrongBounds = aDummyGeometry->GetBounds();
if (i % 2 == 0)
{
wrongBounds[i] -= 1;
}
else
{
wrongBounds[i] += 1;
}
auto wrongGeometry = aDummyGeometry->Clone();
wrongGeometry->SetBounds(wrongBounds);
CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true));
}
}
void IsSubGeometry_Grid_Image()
{
IsSubGeometry_Grid_internal(true);
}
void IsSubGeometry_Grid_NoneImage()
{
IsSubGeometry_Grid_internal(false);
}
void IsSubGeometry_Grid_internal(bool isImage)
{
auto newBounds = aDummyGeometry->GetBounds();
newBounds[0] = 0;
newBounds[1] = 20;
newBounds[2] = 0;
newBounds[3] = 20;
newBounds[4] = 0;
newBounds[5] = 20;
aDummyGeometry->SetBounds(newBounds);
aDummyGeometry->SetImageGeometry(isImage);
auto smallerGeometry = aDummyGeometry->Clone();
newBounds[0] = 5;
newBounds[1] = 10;
newBounds[2] = 5;
newBounds[3] = 10;
newBounds[4] = 5;
newBounds[5] = 10;
smallerGeometry->SetBounds(newBounds);
//legal negative shift
for (unsigned int i = 0; i < 3; ++i)
{
auto legalOrigin = smallerGeometry->GetOrigin();
legalOrigin[i] -= smallerGeometry->GetSpacing()[i];
auto legalGeometry = smallerGeometry->Clone();
legalGeometry->SetOrigin(legalOrigin);
CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometry, mitk::eps, true));
}
//legal positive shift
for (unsigned int i = 0; i < 3; ++i)
{
auto legalOrigin = smallerGeometry->GetOrigin();
legalOrigin[i] += smallerGeometry->GetSpacing()[i];
auto legalGeometry = smallerGeometry->Clone();
legalGeometry->SetOrigin(legalOrigin);
CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometry, mitk::eps, true));
}
//wrong negative shift
for (unsigned int i = 0; i < 3; ++i)
{
auto wrongOrigin = smallerGeometry->GetOrigin();
wrongOrigin[i] -= 2 * mitk::eps;
auto wrongGeometry = smallerGeometry->Clone();
wrongGeometry->SetOrigin(wrongOrigin);
CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true));
}
//wrong positive shift
for (unsigned int i = 0; i < 3; ++i)
{
auto wrongOrigin = smallerGeometry->GetOrigin();
wrongOrigin[i] += 2 * mitk::eps;
auto wrongGeometry = smallerGeometry->Clone();
wrongGeometry->SetOrigin(wrongOrigin);
CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true));
}
}
void IsSubGeometry_Bounds_Oblique_NoneImage()
{
IsSubGeometry_Bounds_Oblique_internal(false);
}
void IsSubGeometry_Bounds_Oblique_Image()
{
IsSubGeometry_Bounds_Oblique_internal(true);
}
void IsSubGeometry_Bounds_Oblique_internal(bool isImage)
{
auto newBounds = aDummyGeometryOblique->GetBounds();
aDummyGeometryOblique->SetImageGeometry(isImage);
//REMARK: used NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION to compensate rounding errors that
//are interoduced when transforming points/indeces due to the oblique geometry.
CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometryOblique, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true));
for (unsigned int i = 0; i < 6; ++i)
{
auto legalBounds = newBounds;
if (i % 2 == 0)
{
legalBounds[i] += 1;
}
else
{
legalBounds[i] -= 1;
}
auto legalGeometry = aDummyGeometryOblique->Clone();
legalGeometry->SetBounds(legalBounds);
CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true));
}
for (unsigned int i = 0; i < 6; ++i)
{
auto wrongBounds = newBounds;
if (i % 2 == 0)
{
wrongBounds[i] -= 1;
}
else
{
wrongBounds[i] += 1;
}
auto wrongGeometry = aDummyGeometryOblique->Clone();
wrongGeometry->SetBounds(wrongBounds);
CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true));
}
}
void IsSubGeometry_Grid_Oblique_NoneImage()
{
IsSubGeometry_Grid_Oblique_internal(false);
}
void IsSubGeometry_Grid_Oblique_Image()
{
IsSubGeometry_Grid_Oblique_internal(true);
}
void IsSubGeometry_Grid_Oblique_internal(bool isImage)
{
auto newBounds = aDummyGeometryOblique->GetBounds();
newBounds[0] = 0;
newBounds[1] = 20;
newBounds[2] = 0;
newBounds[3] = 20;
newBounds[4] = 0;
newBounds[5] = 20;
aDummyGeometryOblique->SetBounds(newBounds);
aDummyGeometryOblique->SetImageGeometry(isImage);
auto smallerGeometry = aDummyGeometryOblique->Clone();
newBounds[0] = 5;
newBounds[1] = 10;
newBounds[2] = 5;
newBounds[3] = 10;
newBounds[4] = 5;
newBounds[5] = 10;
smallerGeometry->SetBounds(newBounds);
//REMARK: used NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION in the following checks
//to compensate rounding errors that are interoduced when transforming points/indeces
//due to the oblique geometry.
//legal negative shift
for (unsigned int i = 0; i < 3; ++i)
{
auto legalOrigin = smallerGeometry->GetOrigin();
mitk::Point3D index;
smallerGeometry->WorldToIndex(legalOrigin, index);
index[i] -= 1;
smallerGeometry->IndexToWorld(index, legalOrigin);
auto legalGeometry = smallerGeometry->Clone();
legalGeometry->SetOrigin(legalOrigin);
CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true));
}
//legal positive shift
for (unsigned int i = 0; i < 3; ++i)
{
auto legalOrigin = smallerGeometry->GetOrigin();
mitk::Point3D index;
smallerGeometry->WorldToIndex(legalOrigin, index);
index[i] += 1;
smallerGeometry->IndexToWorld(index, legalOrigin);
auto legalGeometry = smallerGeometry->Clone();
legalGeometry->SetOrigin(legalOrigin);
CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true));
}
//wrong negative shift
for (unsigned int i = 0; i < 3; ++i)
{
auto wrongOrigin = smallerGeometry->GetOrigin();
wrongOrigin[i] -= 2 * mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION;
auto wrongGeometry = smallerGeometry->Clone();
wrongGeometry->SetOrigin(wrongOrigin);
CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true));
}
//wrong positive shift
for (unsigned int i = 0; i < 3; ++i)
{
auto wrongOrigin = smallerGeometry->GetOrigin();
wrongOrigin[i] += 2 * mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION;
auto wrongGeometry = smallerGeometry->Clone();
wrongGeometry->SetOrigin(wrongOrigin);
CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true));
}
}
}; // end class mitkBaseGeometryTestSuite
MITK_TEST_SUITE_REGISTRATION(mitkBaseGeometry)
diff --git a/Modules/Core/test/mitkIOUtilTest.cpp b/Modules/Core/test/mitkIOUtilTest.cpp
index 31fadfb024..eaad746e4a 100644
--- a/Modules/Core/test/mitkIOUtilTest.cpp
+++ b/Modules/Core/test/mitkIOUtilTest.cpp
@@ -1,311 +1,312 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkTestingMacros.h"
#include <mitkTestFixture.h>
#include <mitkTestingConfig.h>
#include <mitkIOUtil.h>
+#include <mitkUtf8Util.h>
#include <mitkImageGenerator.h>
#include <mitkIOMetaInformationPropertyConstants.h>
#include <mitkVersion.h>
#include <itkMetaDataObject.h>
#include <itkNrrdImageIO.h>
#include <itksys/SystemTools.hxx>
class mitkIOUtilTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkIOUtilTestSuite);
MITK_TEST(TestTempMethods);
MITK_TEST(TestSaveEmptyData);
MITK_TEST(TestLoadAndSaveImage);
MITK_TEST(TestNullLoad);
MITK_TEST(TestNullSave);
MITK_TEST(TestLoadAndSavePointSet);
MITK_TEST(TestLoadAndSaveSurface);
MITK_TEST(TestTempMethodsForUniqueFilenames);
MITK_TEST(TestTempMethodsForUniqueFilenames);
MITK_TEST(TestIOMetaInformation);
MITK_TEST(TestUtf8);
CPPUNIT_TEST_SUITE_END();
private:
std::string m_ImagePath;
std::string m_SurfacePath;
std::string m_PointSetPath;
public:
void setUp() override
{
m_ImagePath = GetTestDataFilePath("Pic3D.nrrd");
m_SurfacePath = GetTestDataFilePath("binary.stl");
m_PointSetPath = GetTestDataFilePath("pointSet.mps");
}
void TestSaveEmptyData()
{
mitk::Surface::Pointer data = mitk::Surface::New();
CPPUNIT_ASSERT_THROW(mitk::IOUtil::Save(data, "/tmp/dummy"), mitk::Exception);
}
void TestTempMethods()
{
std::string tmpPath = mitk::IOUtil::GetTempPath();
CPPUNIT_ASSERT(!tmpPath.empty());
std::ofstream tmpFile;
std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpFile);
CPPUNIT_ASSERT(tmpFile && tmpFile.is_open());
CPPUNIT_ASSERT(tmpFilePath.size() > tmpPath.size());
CPPUNIT_ASSERT(tmpFilePath.substr(0, tmpPath.size()) == tmpPath);
tmpFile.close();
CPPUNIT_ASSERT(std::remove(tmpFilePath.c_str()) == 0);
std::string programPath = mitk::IOUtil::GetProgramPath();
CPPUNIT_ASSERT(!programPath.empty());
std::ofstream tmpFile2;
std::string tmpFilePath2 = mitk::IOUtil::CreateTemporaryFile(tmpFile2, "my-XXXXXX", programPath);
CPPUNIT_ASSERT(tmpFile2 && tmpFile2.is_open());
CPPUNIT_ASSERT(tmpFilePath2.size() > programPath.size());
CPPUNIT_ASSERT(tmpFilePath2.substr(0, programPath.size()) == programPath);
tmpFile2.close();
CPPUNIT_ASSERT(std::remove(tmpFilePath2.c_str()) == 0);
std::ofstream tmpFile3;
std::string tmpFilePath3 =
mitk::IOUtil::CreateTemporaryFile(tmpFile3, std::ios_base::binary, "my-XXXXXX.TXT", programPath);
CPPUNIT_ASSERT(tmpFile3 && tmpFile3.is_open());
CPPUNIT_ASSERT(tmpFilePath3.size() > programPath.size());
CPPUNIT_ASSERT(tmpFilePath3.substr(0, programPath.size()) == programPath);
CPPUNIT_ASSERT(tmpFilePath3.substr(tmpFilePath3.size() - 13, 3) == "my-");
CPPUNIT_ASSERT(tmpFilePath3.substr(tmpFilePath3.size() - 4) == ".TXT");
tmpFile3.close();
// CPPUNIT_ASSERT(std::remove(tmpFilePath3.c_str()) == 0)
std::string tmpFilePath4 = mitk::IOUtil::CreateTemporaryFile();
std::ofstream file;
file.open(tmpFilePath4.c_str());
CPPUNIT_ASSERT_MESSAGE("Testing if file exists after CreateTemporaryFile()", file.is_open());
CPPUNIT_ASSERT_THROW(mitk::IOUtil::CreateTemporaryFile(tmpFile2, "XX"), mitk::Exception);
std::string tmpDir = mitk::IOUtil::CreateTemporaryDirectory();
CPPUNIT_ASSERT(tmpDir.size() > tmpPath.size());
CPPUNIT_ASSERT(tmpDir.substr(0, tmpPath.size()) == tmpPath);
- CPPUNIT_ASSERT(itksys::SystemTools::RemoveADirectory(tmpDir.c_str()));
+ CPPUNIT_ASSERT(itksys::SystemTools::RemoveADirectory(mitk::Utf8Util::Local8BitToUtf8(tmpDir).c_str()));
std::string tmpDir2 = mitk::IOUtil::CreateTemporaryDirectory("my-XXXXXX", programPath);
CPPUNIT_ASSERT(tmpDir2.size() > programPath.size());
CPPUNIT_ASSERT(tmpDir2.substr(0, programPath.size()) == programPath);
- CPPUNIT_ASSERT(itksys::SystemTools::RemoveADirectory(tmpDir2.c_str()));
+ CPPUNIT_ASSERT(itksys::SystemTools::RemoveADirectory(mitk::Utf8Util::Local8BitToUtf8(tmpDir2).c_str()));
}
void TestTempMethodsForUniqueFilenames()
{
int numberOfFiles = 100;
// create 100 empty files
std::vector<std::string> v100filenames;
for (int i = 0; i < numberOfFiles; i++)
{
v100filenames.push_back(mitk::IOUtil::CreateTemporaryFile());
}
// check if all of them are unique
for (int i = 0; i < numberOfFiles; i++)
for (int j = 0; j < numberOfFiles; j++)
{
if (i != j)
{
std::stringstream message;
message << "Checking if file " << i << " and file " << j
<< " are different, which should be the case because each of them should be unique.";
CPPUNIT_ASSERT_MESSAGE(message.str(), (v100filenames.at(i) != v100filenames.at(j)));
}
}
// delete all the files / clean up
for (int i = 0; i < numberOfFiles; i++)
{
std::remove(v100filenames.at(i).c_str());
}
}
void TestLoadAndSaveImage()
{
mitk::Image::Pointer img1 = mitk::IOUtil::Load<mitk::Image>(m_ImagePath);
CPPUNIT_ASSERT(img1.IsNotNull());
std::ofstream tmpStream;
std::string imagePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "diffpic3d-XXXXXX.nrrd");
tmpStream.close();
std::string imagePath2 = mitk::IOUtil::CreateTemporaryFile(tmpStream, "diffpic3d-XXXXXX.nii.gz");
tmpStream.close();
// the cases where no exception should be thrown
CPPUNIT_ASSERT_NO_THROW(mitk::IOUtil::Save(img1, imagePath));
CPPUNIT_ASSERT_NO_THROW(mitk::IOUtil::Save(img1.GetPointer(), imagePath2));
// load data which does not exist
CPPUNIT_ASSERT_THROW(mitk::IOUtil::Load("fileWhichDoesNotExist.nrrd"), mitk::Exception);
// delete the files after the test is done
std::remove(imagePath.c_str());
std::remove(imagePath2.c_str());
mitk::Image::Pointer relativImage = mitk::ImageGenerator::GenerateGradientImage<float>(4, 4, 4, 1);
std::string imagePath3 = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.nrrd");
tmpStream.close();
mitk::IOUtil::Save(relativImage, imagePath3);
CPPUNIT_ASSERT_NO_THROW(mitk::IOUtil::Load(imagePath3));
std::remove(imagePath3.c_str());
}
/**
* \brief This method calls all available load methods with a nullpointer and an empty pathand expects an exception
**/
void TestNullLoad()
{
CPPUNIT_ASSERT_THROW(mitk::IOUtil::Load(""), mitk::Exception);
}
/**
* \brief This method calls the save method (to which all other convenience save methods reference) with null
*parameters
**/
void TestNullSave()
{
CPPUNIT_ASSERT_THROW(mitk::IOUtil::Save(nullptr, mitk::IOUtil::CreateTemporaryFile()), mitk::Exception);
CPPUNIT_ASSERT_THROW(mitk::IOUtil::Save(mitk::Image::New().GetPointer(), ""), mitk::Exception);
}
void TestLoadAndSavePointSet()
{
mitk::PointSet::Pointer pointset = mitk::IOUtil::Load<mitk::PointSet>(m_PointSetPath);
CPPUNIT_ASSERT(pointset.IsNotNull());
std::ofstream tmpStream;
std::string pointSetPath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.mps");
tmpStream.close();
std::string pointSetPathWithDefaultExtension = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.mps");
tmpStream.close();
std::string pointSetPathWithoutDefaultExtension = mitk::IOUtil::CreateTemporaryFile(tmpStream);
tmpStream.close();
// the cases where no exception should be thrown
CPPUNIT_ASSERT_NO_THROW(mitk::IOUtil::Save(pointset, pointSetPathWithDefaultExtension));
// test if defaultextension is inserted if no extension is present
CPPUNIT_ASSERT_NO_THROW(mitk::IOUtil::Save(pointset, pointSetPathWithoutDefaultExtension.c_str()));
// delete the files after the test is done
std::remove(pointSetPath.c_str());
std::remove(pointSetPathWithDefaultExtension.c_str());
std::remove(pointSetPathWithoutDefaultExtension.c_str());
}
void TestLoadAndSaveSurface()
{
mitk::Surface::Pointer surface = mitk::IOUtil::Load<mitk::Surface>(m_SurfacePath);
CPPUNIT_ASSERT(surface.IsNotNull());
std::ofstream tmpStream;
std::string surfacePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "diffsurface-XXXXXX.stl");
// the cases where no exception should be thrown
CPPUNIT_ASSERT_NO_THROW(mitk::IOUtil::Save(surface, surfacePath));
// test if exception is thrown as expected on unknown extsension
CPPUNIT_ASSERT_THROW(mitk::IOUtil::Save(surface, "testSurface.xXx"), mitk::Exception);
// delete the files after the test is done
std::remove(surfacePath.c_str());
}
std::string GenerateMetaDictKey(const mitk::PropertyKeyPath& propKey)
{
auto result = mitk::PropertyKeyPathToPropertyName(propKey);
std::replace(result.begin(), result.end(), '.', '_');
return result;
}
std::string GetValueFromMetaDict(const itk::MetaDataDictionary& dict, const mitk::PropertyKeyPath& propKey)
{
auto metaValueBase = dict.Get(GenerateMetaDictKey(propKey));
auto metaValue = dynamic_cast<const itk::MetaDataObject<std::string>*>(metaValueBase);
return metaValue->GetMetaDataObjectValue();
}
void TestIOMetaInformation()
{
mitk::Image::Pointer img = mitk::IOUtil::Load<mitk::Image>(m_ImagePath);
CPPUNIT_ASSERT(img.IsNotNull());
auto value = img->GetProperty(mitk::PropertyKeyPathToPropertyName(mitk::IOMetaInformationPropertyConstants::READER_DESCRIPTION()).c_str())->GetValueAsString();
CPPUNIT_ASSERT_EQUAL(std::string("ITK NrrdImageIO"), value);
value = img->GetProperty(mitk::PropertyKeyPathToPropertyName(mitk::IOMetaInformationPropertyConstants::READER_INPUTLOCATION()).c_str())->GetValueAsString();
CPPUNIT_ASSERT_EQUAL(m_ImagePath, value);
value = img->GetProperty(mitk::PropertyKeyPathToPropertyName(mitk::IOMetaInformationPropertyConstants::READER_MIME_CATEGORY()).c_str())->GetValueAsString();
CPPUNIT_ASSERT_EQUAL(std::string("Images"), value);
value = img->GetProperty(mitk::PropertyKeyPathToPropertyName(mitk::IOMetaInformationPropertyConstants::READER_MIME_NAME()).c_str())->GetValueAsString();
CPPUNIT_ASSERT_EQUAL(std::string("application/vnd.mitk.image.nrrd"), value);
value = img->GetProperty(mitk::PropertyKeyPathToPropertyName(mitk::IOMetaInformationPropertyConstants::READER_VERSION()).c_str())->GetValueAsString();
CPPUNIT_ASSERT_EQUAL(std::string(MITK_VERSION_STRING), value);
//check if the information is persistet correctly on save.
std::ofstream tmpStream;
std::string imagePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "ioMeta_XXXXXX.nrrd");
tmpStream.close();
mitk::IOUtil::Save(img, imagePath);
auto io = itk::NrrdImageIO::New();
io->SetFileName(imagePath);
io->ReadImageInformation();
auto metaDict = io->GetMetaDataDictionary();
auto metaValue = GetValueFromMetaDict(metaDict, mitk::IOMetaInformationPropertyConstants::READER_DESCRIPTION());
CPPUNIT_ASSERT_EQUAL(std::string("ITK NrrdImageIO"), metaValue);
metaValue = GetValueFromMetaDict(metaDict, mitk::IOMetaInformationPropertyConstants::READER_INPUTLOCATION());
CPPUNIT_ASSERT_EQUAL(m_ImagePath, metaValue);
metaValue = GetValueFromMetaDict(metaDict, mitk::IOMetaInformationPropertyConstants::READER_MIME_CATEGORY());
CPPUNIT_ASSERT_EQUAL(std::string("Images"), metaValue);
metaValue = GetValueFromMetaDict(metaDict, mitk::IOMetaInformationPropertyConstants::READER_MIME_NAME());
CPPUNIT_ASSERT_EQUAL(std::string("application/vnd.mitk.image.nrrd"), metaValue);
metaValue = GetValueFromMetaDict(metaDict, mitk::IOMetaInformationPropertyConstants::READER_VERSION());
CPPUNIT_ASSERT_EQUAL(std::string(MITK_VERSION_STRING), metaValue);
// delete the files after the test is done
std::remove(imagePath.c_str());
}
void TestUtf8()
{
const std::string utf8Path = u8"UTF-8/\u00c4.nrrd"; // LATIN CAPITAL LETTER A WITH DIAERESIS (U+00C4)
- const std::string local8BitPath = mitk::IOUtil::Utf8ToLocal8Bit(utf8Path);
+ const std::string local8BitPath = mitk::Utf8Util::Utf8ToLocal8Bit(utf8Path);
- CPPUNIT_ASSERT(utf8Path == mitk::IOUtil::Local8BitToUtf8(local8BitPath));
+ CPPUNIT_ASSERT(utf8Path == mitk::Utf8Util::Local8BitToUtf8(local8BitPath));
const std::string path = GetTestDataFilePath(local8BitPath);
std::fstream file;
file.open(path);
CPPUNIT_ASSERT(file.is_open());
auto image = mitk::IOUtil::Load<mitk::Image>(path);
CPPUNIT_ASSERT(image.IsNotNull());
}
};
MITK_TEST_SUITE_REGISTRATION(mitkIOUtil)
diff --git a/Modules/Core/test/mitkImageAccessorTest.cpp b/Modules/Core/test/mitkImageAccessorTest.cpp
index e85fb2a72c..4441ac2e42 100644
--- a/Modules/Core/test/mitkImageAccessorTest.cpp
+++ b/Modules/Core/test/mitkImageAccessorTest.cpp
@@ -1,247 +1,234 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
-#include "itkBarrier.h"
#include "mitkIOUtil.h"
#include "mitkImage.h"
#include "mitkImagePixelReadAccessor.h"
#include "mitkImagePixelWriteAccessor.h"
#include "mitkImageReadAccessor.h"
#include "mitkImageTimeSelector.h"
#include "mitkImageWriteAccessor.h"
#include <fstream>
-#include <itkMultiThreader.h>
#include <itksys/SystemTools.hxx>
#include <mitkTestingMacros.h>
#include <cstdlib>
#include <ctime>
+#include <mutex>
+#include <random>
struct ThreadData
{
- itk::Barrier::Pointer m_Barrier; // holds a pointer to the used barrier
- mitk::Image::Pointer data; // some random data
- int m_NoOfThreads; // holds the number of generated threads
- bool m_Successful; // to check if everything worked
-};
-
-itk::SimpleFastMutexLock testMutex;
-
-ITK_THREAD_RETURN_TYPE ThreadMethod(void *data)
-{
- /* extract data pointer from Thread Info structure */
- auto *pInfo = (struct itk::MultiThreader::ThreadInfoStruct *)data;
-
- // some data validity checking
- if (pInfo == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
- if (pInfo->UserData == nullptr)
+ ThreadData()
+ : Successful(true),
+ RandGen(std::random_device()())
{
- return ITK_THREAD_RETURN_VALUE;
}
- // obtain user data for processing
- auto *threadData = (ThreadData *)pInfo->UserData;
+ mitk::Image::Pointer Data; // some random data
+ bool Successful; // to check if everything worked
+ std::mt19937 RandGen;
+ std::mutex RandGenMutex;
+};
- srand(pInfo->ThreadID);
+std::mutex testMutex;
- mitk::Image::Pointer im = threadData->data;
+itk::ITK_THREAD_RETURN_TYPE ThreadMethod(ThreadData *threadData)
+{
+ if (nullptr == threadData)
+ return itk::ITK_THREAD_RETURN_DEFAULT_VALUE;
+
+ mitk::Image::Pointer im = threadData->Data;
int nrSlices = im->GetDimension(2);
+ std::uniform_int_distribution<> distrib(1, 1000000);
+
// Create randomly a PixelRead- or PixelWriteAccessor for a slice and access all pixels in it.
try
{
- if (rand() % 2)
+ threadData->RandGenMutex.lock();
+ auto even = distrib(threadData->RandGen) % 2;
+ auto slice = distrib(threadData->RandGen) % nrSlices;
+ threadData->RandGenMutex.unlock();
+
+ if (even)
{
- testMutex.Lock();
- mitk::ImageDataItem *iDi = im->GetSliceData(rand() % nrSlices);
- testMutex.Unlock();
+ testMutex.lock();
+ mitk::ImageDataItem *iDi = im->GetSliceData(slice);
+ testMutex.unlock();
while (!iDi->IsComplete())
{
}
// MITK_INFO << "pixeltype: " << im->GetPixelType().GetComponentTypeAsString();
if ((im->GetPixelType().GetComponentTypeAsString() == "short") && (im->GetDimension() == 3))
{
// Use pixeltype&dimension specific read accessor
int xlength = im->GetDimension(0);
int ylength = im->GetDimension(1);
mitk::ImagePixelReadAccessor<short, 2> readAccessor(im, iDi);
itk::Index<2> idx;
for (int i = 0; i < xlength; ++i)
{
for (int j = 0; j < ylength; ++j)
{
idx[0] = i;
idx[1] = j;
readAccessor.GetPixelByIndexSafe(idx);
}
}
}
else
{
// use general accessor
mitk::ImageReadAccessor *iRA = new mitk::ImageReadAccessor(im, iDi);
delete iRA;
}
}
else
{
- testMutex.Lock();
- mitk::ImageDataItem *iDi = im->GetSliceData(rand() % nrSlices);
- testMutex.Unlock();
+ testMutex.lock();
+ mitk::ImageDataItem *iDi = im->GetSliceData(slice);
+ testMutex.unlock();
while (!iDi->IsComplete())
{
}
if ((im->GetPixelType().GetComponentTypeAsString() == "short") && (im->GetDimension() == 3))
{
// Use pixeltype&dimension specific read accessor
int xlength = im->GetDimension(0);
int ylength = im->GetDimension(1);
mitk::ImagePixelWriteAccessor<short, 2> writeAccessor(im, iDi);
itk::Index<2> idx;
for (int i = 0; i < xlength; ++i)
{
for (int j = 0; j < ylength; ++j)
{
idx[0] = i;
idx[1] = j;
- short newVal = rand() % 16000;
+ threadData->RandGenMutex.lock();
+ short newVal = distrib(threadData->RandGen) % 16000;
+ threadData->RandGenMutex.unlock();
writeAccessor.SetPixelByIndexSafe(idx, newVal);
short val = writeAccessor.GetPixelByIndexSafe(idx);
if (val != newVal)
{
- threadData->m_Successful = false;
+ threadData->Successful = false;
}
}
}
}
else
{
// use general accessor
mitk::ImageWriteAccessor iB(im, iDi);
void *pointer = iB.GetData();
*((char *)pointer) = 0;
}
}
}
catch ( const mitk::MemoryIsLockedException &e )
{
- threadData->m_Successful = false;
+ threadData->Successful = false;
e.Print(std::cout);
}
catch ( const mitk::Exception &e )
{
- threadData->m_Successful = false;
+ threadData->Successful = false;
e.Print(std::cout);
}
- // data processing end!
- threadData->m_Barrier->Wait();
- return ITK_THREAD_RETURN_VALUE;
+ return itk::ITK_THREAD_RETURN_DEFAULT_VALUE;
}
int mitkImageAccessorTest(int argc, char *argv[])
{
MITK_TEST_BEGIN("mitkImageAccessorTest");
std::cout << "Loading file: ";
if (argc == 0)
{
std::cout << "no file specified [FAILED]" << std::endl;
return EXIT_FAILURE;
}
mitk::Image::Pointer image = nullptr;
try
{
image = mitk::IOUtil::Load<mitk::Image>(std::string(argv[1]));
if (image.IsNull())
{
MITK_TEST_FAILED_MSG(<< "file could not be loaded [FAILED]")
}
}
catch ( const itk::ExceptionObject &ex )
{
MITK_TEST_FAILED_MSG(<< "Exception: " << ex.GetDescription() << "[FAILED]")
}
// CHECK INAPPROPRIATE AND SPECIAL USAGE
// recursive mutex lock
MITK_TEST_OUTPUT(<< "Testing a recursive mutex lock attempt, should end in an exception ...");
MITK_TEST_FOR_EXCEPTION_BEGIN(mitk::Exception)
mitk::ImageWriteAccessor first(image);
mitk::ImageReadAccessor second(image);
MITK_TEST_FOR_EXCEPTION_END(mitk::Exception)
// ignore lock mechanism in read accessor
try
{
mitk::ImageWriteAccessor first(image);
mitk::ImageReadAccessor second(image, nullptr, mitk::ImageAccessorBase::IgnoreLock);
MITK_TEST_CONDITION_REQUIRED(true, "Testing the option flag \"IgnoreLock\" in ReadAccessor");
}
catch (const mitk::Exception & /*e*/)
{
MITK_TEST_CONDITION_REQUIRED(false, "Ignoring the lock mechanism leads to exception.");
}
// CREATE THREADS
image->GetGeometry()->Initialize();
- itk::MultiThreader::Pointer threader = itk::MultiThreader::New();
- unsigned int noOfThreads = 100;
+ ThreadData threadData;
+ threadData.Data = image;
- // initialize barrier
- itk::Barrier::Pointer barrier = itk::Barrier::New();
- barrier->Initialize(noOfThreads + 1); // add one for we stop the base thread when the worker threads are processing
-
- auto *threadData = new ThreadData;
- threadData->m_Barrier = barrier;
- threadData->m_NoOfThreads = noOfThreads;
- threadData->data = image;
- threadData->m_Successful = true;
+ constexpr size_t noOfThreads = 100;
+ std::array<std::thread, noOfThreads> threads;
// spawn threads
- for (unsigned int i = 0; i < noOfThreads; ++i)
- {
- threader->SpawnThread(ThreadMethod, threadData);
- }
- // stop the base thread during worker thread execution
- barrier->Wait();
+ for (size_t i = 0; i < noOfThreads; ++i)
+ threads[i] = std::thread(ThreadMethod, &threadData);
// terminate threads
- for (unsigned int j = 0; j < noOfThreads; ++j)
+ for (size_t i = 0; i < noOfThreads; ++i)
{
- threader->TerminateThread(j);
+ if (threads[i].joinable())
+ threads[i].join();
}
- bool TestSuccessful = threadData->m_Successful;
- delete threadData;
+ bool TestSuccessful = threadData.Successful;
MITK_TEST_CONDITION_REQUIRED(TestSuccessful, "Testing image access from multiple threads");
MITK_TEST_END();
}
diff --git a/Modules/Core/test/mitkImageDimensionConverterTest.cpp b/Modules/Core/test/mitkImageDimensionConverterTest.cpp
index 85330086ef..2428741336 100644
--- a/Modules/Core/test/mitkImageDimensionConverterTest.cpp
+++ b/Modules/Core/test/mitkImageDimensionConverterTest.cpp
@@ -1,260 +1,260 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// mitk includes
#include "mitkTestingConfig.h"
#include <mitkConvert2Dto3DImageFilter.h>
#include <mitkIOUtil.h>
#include <mitkImage.h>
#include <mitkImageCast.h>
#include <mitkImageDataItem.h>
#include <mitkImageStatisticsHolder.h>
#include <mitkInteractionConst.h>
#include <mitkPlaneOperation.h>
#include <mitkRotationOperation.h>
#include <mitkTestingMacros.h>
// itk includes
#include <itkImage.h>
#include <itkMersenneTwisterRandomVariateGenerator.h>
// stl includes
#include <fstream>
// vtk includes
#include <vtkImageData.h>
int mitkImageDimensionConverterTest(int /*argc*/, char * /*argv*/ [])
{
MITK_TEST_BEGIN(mitkImageDimensionConverterTest);
// Define an epsilon which is the allowed error
float eps = 0.00001;
// Define helper variables
float error = 0;
bool matrixIsEqual = true;
std::stringstream sstream;
mitk::Convert2Dto3DImageFilter::Pointer convertFilter = mitk::Convert2Dto3DImageFilter::New();
///////////////////////////////////////
// Create 2D Image with a 3D rotation from scratch.
typedef itk::Image<double, 2> ImageType;
ImageType::Pointer itkImage = ImageType::New();
ImageType::RegionType myRegion;
ImageType::SizeType mySize;
ImageType::IndexType myIndex;
ImageType::SpacingType mySpacing;
mySpacing[0] = 1;
mySpacing[1] = 1;
myIndex[0] = 0;
myIndex[1] = 0;
mySize[0] = 50;
mySize[1] = 50;
myRegion.SetSize(mySize);
myRegion.SetIndex(myIndex);
itkImage->SetSpacing(mySpacing);
itkImage->SetRegions(myRegion);
itkImage->Allocate();
itkImage->FillBuffer(50);
mitk::Image::Pointer mitkImage2D;
mitk::CastToMitkImage(itkImage, mitkImage2D);
// rotate
mitk::Point3D p;
p[0] = 1;
p[1] = 3;
p[2] = 5;
mitk::Vector3D v;
v[0] = 0.3;
v[1] = 1;
v[2] = 0.1;
mitk::RotationOperation op(mitk::OpROTATE, p, v, 35);
mitkImage2D->GetGeometry()->ExecuteOperation(&op);
// Save original Geometry infos
mitk::Vector3D Original_col0, Original_col1, Original_col2;
Original_col0.SetVnlVector(
- mitkImage2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0));
+ mitkImage2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).as_ref());
Original_col1.SetVnlVector(
- mitkImage2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1));
+ mitkImage2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).as_ref());
Original_col2.SetVnlVector(
- mitkImage2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2));
+ mitkImage2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref());
MITK_INFO << "Rotated Matrix: " << Original_col0 << " " << Original_col1 << " " << Original_col2;
mitk::Point3D Original_Origin = mitkImage2D->GetGeometry()->GetOrigin();
mitk::Vector3D Original_Spacing = mitkImage2D->GetGeometry()->GetSpacing();
MITK_TEST_CONDITION_REQUIRED(mitkImage2D->GetDimension() == 2, "Created Image is Dimension 2");
///////////////////////////////////////
// mitkImage2D is now a 2D image with 3D Geometry information.
// Save it without conversion and load it again. It should have an identitiy matrix
sstream.clear();
sstream << MITK_TEST_OUTPUT_DIR << ""
<< "/rotatedImage2D.nrrd";
mitk::IOUtil::Save(mitkImage2D, sstream.str());
mitk::Image::Pointer imageLoaded2D = mitk::IOUtil::Load<mitk::Image>(sstream.str());
// check if image can be loaded
MITK_TEST_CONDITION_REQUIRED(imageLoaded2D.IsNotNull(), "Loading saved 2D Image");
MITK_TEST_CONDITION_REQUIRED(imageLoaded2D->GetDimension() == 2, "Loaded Image is Dimension 2");
// check if spacing is ok
mitk::Vector3D Loaded2D_Spacing = imageLoaded2D->GetGeometry()->GetSpacing();
error = std::fabs(Loaded2D_Spacing[0] - Original_Spacing[0]) + std::fabs(Loaded2D_Spacing[1] - Original_Spacing[1]) +
std::fabs(Loaded2D_Spacing[2] - 1);
MITK_TEST_CONDITION_REQUIRED(error < eps, "Compare Geometry: Spacing");
// Check origin
mitk::Point3D Loaded2D_Origin = imageLoaded2D->GetGeometry()->GetOrigin();
error = std::fabs(Loaded2D_Origin[0] - Original_Origin[0]) + std::fabs(Loaded2D_Origin[1] - Original_Origin[1]) +
std::fabs(Loaded2D_Origin[2] - 0);
MITK_TEST_CONDITION_REQUIRED(error < eps, "Compare Geometry: Origin");
// Check matrix
mitk::Vector3D Loaded2D_col0, Loaded2D_col1, Loaded2D_col2;
Loaded2D_col0.SetVnlVector(
- imageLoaded2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0));
+ imageLoaded2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).as_ref());
Loaded2D_col1.SetVnlVector(
- imageLoaded2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1));
+ imageLoaded2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).as_ref());
Loaded2D_col2.SetVnlVector(
- imageLoaded2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2));
+ imageLoaded2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref());
if ((std::fabs(1 - Loaded2D_col0[0]) > eps) || (std::fabs(0 - Loaded2D_col0[1]) > eps) ||
(std::fabs(0 - Loaded2D_col0[2]) > eps) || (std::fabs(0 - Loaded2D_col1[0]) > eps) ||
(std::fabs(1 - Loaded2D_col1[1]) > eps) || (std::fabs(0 - Loaded2D_col1[2]) > eps) ||
(std::fabs(0 - Loaded2D_col2[0]) > eps) || (std::fabs(0 - Loaded2D_col2[1]) > eps) ||
(std::fabs(1 - Loaded2D_col2[2]) > eps))
{
matrixIsEqual = false;
}
else
matrixIsEqual = true;
MITK_TEST_CONDITION_REQUIRED(matrixIsEqual, "Compare Geometry: Matrix");
///////////////////////////////////////
// mitkImage2D is a 2D image with 3D Geometry information.
// Convert it with filter to a 3D image and check if everything went well
convertFilter->SetInput(mitkImage2D);
convertFilter->Update();
mitk::Image::Pointer mitkImage3D = convertFilter->GetOutput();
MITK_TEST_CONDITION_REQUIRED(mitkImage3D->GetDimension() == 3, "Converted Image is Dimension 3");
// check if geometry is still same
mitk::Vector3D Converted_Spacing = mitkImage3D->GetGeometry()->GetSpacing();
error = std::fabs(Converted_Spacing[0] - Original_Spacing[0]) +
std::fabs(Converted_Spacing[1] - Original_Spacing[1]) + std::fabs(Converted_Spacing[2] - Original_Spacing[2]);
MITK_TEST_CONDITION_REQUIRED(error < eps, "Compare Geometry: Spacing");
mitk::Point3D Converted_Origin = mitkImage3D->GetGeometry()->GetOrigin();
error = std::fabs(Converted_Origin[0] - Original_Origin[0]) + std::fabs(Converted_Origin[1] - Original_Origin[1]) +
std::fabs(Converted_Origin[2] - Original_Origin[2]);
MITK_INFO << Converted_Origin << " and " << Original_Origin;
MITK_TEST_CONDITION_REQUIRED(error < eps, "Compare Geometry: Origin");
mitk::Vector3D Converted_col0, Converted_col1, Converted_col2;
Converted_col0.SetVnlVector(
- mitkImage3D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0));
+ mitkImage3D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).as_ref());
Converted_col1.SetVnlVector(
- mitkImage3D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1));
+ mitkImage3D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).as_ref());
Converted_col2.SetVnlVector(
- mitkImage3D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2));
+ mitkImage3D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref());
if ((std::fabs(Original_col0[0] - Converted_col0[0]) > eps) ||
(std::fabs(Original_col0[1] - Converted_col0[1]) > eps) ||
(std::fabs(Original_col0[2] - Converted_col0[2]) > eps) ||
(std::fabs(Original_col1[0] - Converted_col1[0]) > eps) ||
(std::fabs(Original_col1[1] - Converted_col1[1]) > eps) ||
(std::fabs(Original_col1[2] - Converted_col1[2]) > eps) ||
(std::fabs(Original_col2[0] - Converted_col2[0]) > eps) ||
(std::fabs(Original_col2[1] - Converted_col2[1]) > eps) ||
(std::fabs(Original_col2[2] - Converted_col2[2]) > eps))
{
MITK_INFO << "Oh No! Original Image Matrix and Converted Image Matrix are different!";
MITK_INFO << "original Image:" << Original_col0 << " " << Original_col1 << " " << Original_col2;
MITK_INFO << "converted Image:" << Converted_col0 << " " << Converted_col1 << " " << Converted_col2;
matrixIsEqual = false;
}
else
matrixIsEqual = true;
MITK_TEST_CONDITION_REQUIRED(matrixIsEqual, "Compare Geometry: Matrix");
///////////////////////////////////////
// So far it seems good! now try to save and load the file
std::stringstream sstream2;
sstream2 << MITK_TEST_OUTPUT_DIR << ""
<< "/rotatedImage.nrrd";
mitk::IOUtil::Save(mitkImage3D, sstream2.str());
mitk::Image::Pointer imageLoaded = mitk::IOUtil::Load<mitk::Image>(sstream2.str());
// check if image can be loaded
MITK_TEST_CONDITION_REQUIRED(imageLoaded.IsNotNull(), "Loading saved Image");
// check if loaded image is still what it should be:
MITK_TEST_CONDITION_REQUIRED(imageLoaded->GetDimension() == 3, "Loaded Image is Dimension 3");
// check if geometry is still same
mitk::Vector3D Loaded_Spacing = imageLoaded->GetGeometry()->GetSpacing();
error = std::fabs(Loaded_Spacing[0] - Original_Spacing[0]) + std::fabs(Loaded_Spacing[1] - Original_Spacing[1]) +
std::fabs(Loaded_Spacing[2] - Original_Spacing[2]);
MITK_TEST_CONDITION_REQUIRED(error < eps, "Compare Geometry: Spacing");
mitk::Point3D Loaded_Origin = imageLoaded->GetGeometry()->GetOrigin();
error = std::fabs(Loaded_Origin[0] - Original_Origin[0]) + std::fabs(Loaded_Origin[1] - Original_Origin[1]) +
std::fabs(Loaded_Origin[2] - Original_Origin[2]);
MITK_TEST_CONDITION_REQUIRED(error < eps, "Compare Geometry: Origin");
mitk::Vector3D Loaded_col0, Loaded_col1, Loaded_col2;
Loaded_col0.SetVnlVector(
- imageLoaded->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0));
+ imageLoaded->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).as_ref());
Loaded_col1.SetVnlVector(
- imageLoaded->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1));
+ imageLoaded->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).as_ref());
Loaded_col2.SetVnlVector(
- imageLoaded->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2));
+ imageLoaded->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref());
if ((std::fabs(Original_col0[0] - Loaded_col0[0]) > eps) || (std::fabs(Original_col0[1] - Loaded_col0[1]) > eps) ||
(std::fabs(Original_col0[2] - Loaded_col0[2]) > eps) || (std::fabs(Original_col1[0] - Loaded_col1[0]) > eps) ||
(std::fabs(Original_col1[1] - Loaded_col1[1]) > eps) || (std::fabs(Original_col1[2] - Loaded_col1[2]) > eps) ||
(std::fabs(Original_col2[0] - Loaded_col2[0]) > eps) || (std::fabs(Original_col2[1] - Loaded_col2[1]) > eps) ||
(std::fabs(Original_col2[2] - Loaded_col2[2]) > eps))
{
MITK_INFO << "Oh No! Original Image Matrix and Converted Image Matrix are different!";
MITK_INFO << "original Image:" << Original_col0 << " " << Original_col1 << " " << Original_col2;
MITK_INFO << "converted Image:" << Loaded_col0 << " " << Loaded_col1 << " " << Loaded_col2;
matrixIsEqual = false;
}
else
matrixIsEqual = true;
MITK_TEST_CONDITION_REQUIRED(matrixIsEqual, "Compare Geometry: Matrix");
return 0;
MITK_TEST_END();
}
diff --git a/Modules/Core/test/mitkImageGeneratorTest.cpp b/Modules/Core/test/mitkImageGeneratorTest.cpp
index 57dc747de6..cc106d629a 100644
--- a/Modules/Core/test/mitkImageGeneratorTest.cpp
+++ b/Modules/Core/test/mitkImageGeneratorTest.cpp
@@ -1,327 +1,327 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// Testing
#include "mitkTestFixture.h"
#include <mitkTestingMacros.h>
// MITK includes
#include <mitkCoreServices.h>
#include "mitkImage.h"
#include "mitkImageGenerator.h"
#include "mitkImageReadAccessor.h"
#include "mitkImageStatisticsHolder.h"
class mitkImageGeneratorTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkImageGeneratorTestSuite);
MITK_TEST(SetSpacingX2D_Success);
MITK_TEST(SetDefaultSpacingX2D_Success);
MITK_TEST(SetSpacingX3D_Success);
MITK_TEST(SetSpacingY2D_Success);
MITK_TEST(SetDefaultSpacingY2D_Success);
MITK_TEST(SetSpacingY3D_Success);
MITK_TEST(SetSpacingZ2D_Success);
MITK_TEST(SetDefaultSpacingZ2D_Success);
MITK_TEST(SetSpacingZ3D_Success);
MITK_TEST(SetDimension2D_Success);
MITK_TEST(SetDimension3D_Success);
MITK_TEST(SetDimension4D_Success);
MITK_TEST(SetDimensionX2D_Success);
MITK_TEST(SetDimensionY2D_Success);
MITK_TEST(SetDimensionZ3D_Success);
MITK_TEST(SetDimensionT4D_Success);
MITK_TEST(SetDimensions3Dc_Success);
MITK_TEST(SetDataTypeFloat2D_Success);
MITK_TEST(SetDataTypeUChar2D_Success);
MITK_TEST(SetDataTypeInt3D_Success);
MITK_TEST(SetDataTypeDouble3D_Success);
MITK_TEST(SetDataTypeFloat4D_Success);
MITK_TEST(SetDataTypeUChar4D_Success);
MITK_TEST(SetDataTypeUInt3D_Success);
MITK_TEST(SetPixelTypeFloat2D_Success);
MITK_TEST(SetPixelTypeUChar2D_Success);
MITK_TEST(SetPixelTypeInt3D_Success);
MITK_TEST(SetPixelTypeDouble3D_Success);
MITK_TEST(SetPixelTypeFloat4D_Success);
MITK_TEST(SetPixelTypeUChar4D_Success);
MITK_TEST(SetPixelTypeUInt3D_Success);
MITK_TEST(MaxValueHolds_Success);
MITK_TEST(MinValueHolds_Success);
MITK_TEST(DefaultMaxValueHolds_Success);
MITK_TEST(DefaultMinValueHolds_Success);
MITK_TEST(SetGradientImageValues_Success);
CPPUNIT_TEST_SUITE_END();
private:
// create some images with arbitrary parameters (corner cases)
mitk::Image::Pointer m_Image2Da;
mitk::Image::Pointer m_Image2Db;
mitk::Image::Pointer m_Image3Da;
mitk::Image::Pointer m_Image3Db;
mitk::Image::Pointer m_Image4Da;
mitk::Image::Pointer m_Image4Db;
mitk::Image::Pointer m_Image3Dc;
public:
void setUp()
{
m_Image2Da = mitk::ImageGenerator::GenerateRandomImage<float>(2, 4, 0, 0, 0.1, 0.2, 0.3, 577, 23);
m_Image2Db = mitk::ImageGenerator::GenerateRandomImage<unsigned char>(1, 1, 0, 0);
m_Image3Da = mitk::ImageGenerator::GenerateRandomImage<int>(2, 4, 1, 0);
m_Image3Db = mitk::ImageGenerator::GenerateRandomImage<double>(2, 4, 8, 0);
m_Image4Da = mitk::ImageGenerator::GenerateRandomImage<float>(2, 4, 8, 1);
m_Image4Db = mitk::ImageGenerator::GenerateRandomImage<unsigned char>(2, 4, 8, 2);
m_Image3Dc = mitk::ImageGenerator::GenerateGradientImage<unsigned int>(1, 2, 3, 4, 5, 6);
}
void tearDown()
{
m_Image2Da = nullptr;
m_Image2Db = nullptr;
m_Image3Da = nullptr;
m_Image3Db = nullptr;
m_Image4Da = nullptr;
m_Image4Db = nullptr;
m_Image3Dc = nullptr;
}
void SetSpacingX2D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if spacing 2D x is set correctly.",
fabs(m_Image2Da->GetGeometry()->GetSpacing()[0] - 0.1) < 0.0001);
}
void SetDefaultSpacingX2D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if default spacing 2D x is set correctly.",
fabs(m_Image2Db->GetGeometry()->GetSpacing()[0] - 1.0) < 0.0001);
}
void SetSpacingX3D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if spacing 3D x is set correctly.",
fabs(m_Image3Dc->GetGeometry()->GetSpacing()[0] - 4) < 0.0001);
}
void SetSpacingY2D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if spacing 2D y is set correctly.",
fabs(m_Image2Da->GetGeometry()->GetSpacing()[1] - 0.2) < 0.0001);
}
void SetDefaultSpacingY2D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if default spacing 2D y is set correctly.",
fabs(m_Image2Db->GetGeometry()->GetSpacing()[1] - 1.0) < 0.0001);
}
void SetSpacingY3D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if spacing 3D y is set correctly.",
fabs(m_Image3Dc->GetGeometry()->GetSpacing()[1] - 5) < 0.0001);
}
void SetSpacingZ2D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if spacing 2D z is set correctly.",
fabs(m_Image2Da->GetGeometry()->GetSpacing()[2] - 0.3) < 0.0001);
}
void SetDefaultSpacingZ2D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if default spacing 2D z is set correctly.",
fabs(m_Image2Db->GetGeometry()->GetSpacing()[2] - 1.0) < 0.0001);
}
void SetSpacingZ3D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if spacing z is set correctly.",
fabs(m_Image3Dc->GetGeometry()->GetSpacing()[2] - 6) < 0.0001);
}
void SetDimension2D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if the dimension 2D is set correctly.", m_Image2Da->GetDimension() == 2);
CPPUNIT_ASSERT_MESSAGE("Testing if the dimension 2D is set correctly.", m_Image2Db->GetDimension() == 2);
}
void SetDimension3D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if the dimension 3D is set correctly.",m_Image3Da->GetDimension() == 2);
CPPUNIT_ASSERT_MESSAGE("Testing if the dimension 3D is set correctly.", m_Image3Db->GetDimension() == 3);
}
void SetDimension4D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if the dimension 4D is set correctly.", m_Image4Da->GetDimension() == 3);
CPPUNIT_ASSERT_MESSAGE("Testing if the dimension 4D is set correctly.", m_Image4Db->GetDimension() == 4);
}
void SetDimensionX2D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if the X dimension of the 2D image is set correctly.", m_Image2Da->GetDimension(0) == 2);
}
void SetDimensionY2D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if the Y dimension of the 2D image is set correctly.", m_Image2Db->GetDimension(1) == 1);
}
void SetDimensionZ3D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if the Z dimension of the 3D image is set correctly.", m_Image3Da->GetDimension(2) == 1);
CPPUNIT_ASSERT_MESSAGE("Testing if the Z dimension of the 3D image is set correctly.", m_Image3Db->GetDimension(2) == 8);
}
void SetDimensionT4D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if the T dimension of the 4D image is set correctly.", m_Image4Da->GetDimension(3) == 1);
CPPUNIT_ASSERT_MESSAGE("Testing if the T dimension of the 4D image is set correctly.", m_Image4Db->GetDimension(3) == 2);
}
void SetDimensions3Dc_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if image3Dc dimension x is set correctly.", m_Image3Dc->GetDimension(0) == 1);
CPPUNIT_ASSERT_MESSAGE("Testing if image3Dc dimension y is set correctly.", m_Image3Dc->GetDimension(1) == 2);
CPPUNIT_ASSERT_MESSAGE("Testing if image3Dc dimension z is set correctly.", m_Image3Dc->GetDimension(2) == 3);
}
void SetDataTypeFloat2D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a float 3D image is set correctly.",
- m_Image2Da->GetPixelType().GetComponentType() == itk::ImageIOBase::FLOAT);
+ m_Image2Da->GetPixelType().GetComponentType() == itk::IOComponentEnum::FLOAT);
}
void SetDataTypeUChar2D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a UChar 2D image is set correctly.",
- m_Image2Db->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR);
+ m_Image2Db->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR);
}
void SetDataTypeInt3D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a Int 3D image is set correctly.",
- m_Image3Da->GetPixelType().GetComponentType() == itk::ImageIOBase::INT);
+ m_Image3Da->GetPixelType().GetComponentType() == itk::IOComponentEnum::INT);
}
void SetDataTypeDouble3D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a Double 3D image is set correctly.",
- m_Image3Db->GetPixelType().GetComponentType() == itk::ImageIOBase::DOUBLE);
+ m_Image3Db->GetPixelType().GetComponentType() == itk::IOComponentEnum::DOUBLE);
}
void SetDataTypeFloat4D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a Float 4D image is set correctly.",
- m_Image4Da->GetPixelType().GetComponentType() == itk::ImageIOBase::FLOAT);
+ m_Image4Da->GetPixelType().GetComponentType() == itk::IOComponentEnum::FLOAT);
}
void SetDataTypeUChar4D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a UChar 4D image is set correctly.",
- m_Image4Db->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR);
+ m_Image4Db->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR);
}
void SetDataTypeUInt3D_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a UInt 3D image is set correctly.",
- m_Image3Dc->GetPixelType().GetComponentType() == itk::ImageIOBase::UINT);
+ m_Image3Dc->GetPixelType().GetComponentType() == itk::IOComponentEnum::UINT);
}
void SetPixelTypeFloat2D_Success()
{
- itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR;
+ auto scalarType = itk::IOPixelEnum::SCALAR;
CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a Float 2D image is set correctly.",
m_Image2Da->GetPixelType().GetPixelType() == scalarType);
}
void SetPixelTypeUChar2D_Success()
{
- itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR;
+ auto scalarType = itk::IOPixelEnum::SCALAR;
CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a UChar 2D image is set correctly.",
m_Image2Db->GetPixelType().GetPixelType() == scalarType);
}
void SetPixelTypeInt3D_Success()
{
- itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR;
+ auto scalarType = itk::IOPixelEnum::SCALAR;
CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a Int 3D image is set correctly.",
m_Image3Da->GetPixelType().GetPixelType() == scalarType);
}
void SetPixelTypeDouble3D_Success()
{
- itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR;
+ auto scalarType = itk::IOPixelEnum::SCALAR;
CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a Double 3D image is set correctly.",
m_Image3Db->GetPixelType().GetPixelType() == scalarType);
}
void SetPixelTypeFloat4D_Success()
{
- itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR;
+ auto scalarType = itk::IOPixelEnum::SCALAR;
CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a Float 4D image is set correctly.",
m_Image4Da->GetPixelType().GetPixelType() == scalarType);
}
void SetPixelTypeUChar4D_Success()
{
- itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR;
+ auto scalarType = itk::IOPixelEnum::SCALAR;
CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a UChar 4D image is set correctly.",
m_Image4Db->GetPixelType().GetPixelType() == scalarType);
}
void SetPixelTypeUInt3D_Success()
{
- itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR;
+ auto scalarType = itk::IOPixelEnum::SCALAR;
CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a UInt 3D image is set correctly.",
m_Image3Dc->GetPixelType().GetPixelType() == scalarType);
}
void MaxValueHolds_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if max value holds", m_Image2Da->GetStatistics()->GetScalarValueMax() <= 577);
}
void MinValueHolds_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if min value holds", m_Image2Da->GetStatistics()->GetScalarValueMin() >= 23);
}
void DefaultMaxValueHolds_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if default max value holds", m_Image3Da->GetStatistics()->GetScalarValueMax() <= 1000);
}
void DefaultMinValueHolds_Success()
{
CPPUNIT_ASSERT_MESSAGE("Testing if default min value holds", m_Image3Da->GetStatistics()->GetScalarValueMin() >= 0);
}
void SetGradientImageValues_Success()
{
const unsigned int *image3DcBuffer = nullptr;
try
{
mitk::ImageReadAccessor readAccess(m_Image3Dc);
image3DcBuffer = static_cast<const unsigned int *>(readAccess.GetData());
}
catch (...)
{
MITK_ERROR << "Read access not granted on mitk::Image.";
}
for (unsigned int i = 0; i < 2 * 3; i++)
{
CPPUNIT_ASSERT_MESSAGE("Testing if gradient image values are set correctly", image3DcBuffer[i] == i);
}
}
};
MITK_TEST_SUITE_REGISTRATION(mitkImageGenerator)
diff --git a/Modules/Core/test/mitkImportItkImageTest.cpp b/Modules/Core/test/mitkImportItkImageTest.cpp
index 90afbe80f3..1ce29b55b1 100644
--- a/Modules/Core/test/mitkImportItkImageTest.cpp
+++ b/Modules/Core/test/mitkImportItkImageTest.cpp
@@ -1,306 +1,306 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkITKImageImport.h"
#include "mitkImageCast.h"
#include "mitkTestingMacros.h"
#include "mitkImagePixelReadAccessor.h"
#include <itkImageRegionConstIteratorWithIndex.h>
#include <itkRandomImageSource.h>
/**
* Create a test image with random pixel values. The image size is determined by the input parameter.
*
* @param size the number of voxels in each dimension
*/
template <typename TPixel, unsigned int VDimension>
typename itk::Image<TPixel, VDimension>::Pointer CreateTestImageRandom(short int size)
{
typedef typename itk::Image<TPixel, VDimension> ImageType;
itk::Size<VDimension> regionSize;
regionSize.Fill(size);
typename itk::RandomImageSource<ImageType>::Pointer randomImageSource = itk::RandomImageSource<ImageType>::New();
- randomImageSource->SetNumberOfThreads(1); // to produce non-random results
+ randomImageSource->SetNumberOfWorkUnits(1); // to produce non-random results
randomImageSource->SetSize(regionSize);
randomImageSource->Update();
return randomImageSource->GetOutput();
}
/**
* Create a test vector image (with two components) with a single pixel value. The image size is determined by the input
* parameter.
*
* @param value the pixel value the created image is filled with
* @param size the number of voxels in each dimension
*/
template <typename TPixel, unsigned int VDimension>
typename itk::VectorImage<TPixel, VDimension>::Pointer CreateTestVectorImageFixedValue(
size_t size, const itk::VariableLengthVector<TPixel> &value)
{
typedef typename itk::VectorImage<TPixel, VDimension> ImageType;
typedef typename ImageType::Pointer ImagePointer;
typename ImageType::RegionType imageRegion;
typename ImageType::RegionType::SizeType regionSize;
regionSize.Fill(size);
typename ImageType::RegionType::IndexType regionIndex;
regionIndex.Fill(0);
imageRegion.SetSize(regionSize);
imageRegion.SetIndex(regionIndex);
typename ImageType::SpacingType imageSpacing;
imageSpacing.Fill(1.0f);
typename ImageType::PointType imageOrigin;
imageOrigin.Fill(0.0f);
ImagePointer itkImage = ImageType::New();
itkImage->SetVectorLength(value.GetNumberOfElements());
itkImage->SetRegions(imageRegion);
itkImage->SetOrigin(imageOrigin);
itkImage->SetSpacing(imageSpacing);
itkImage->Allocate();
itkImage->FillBuffer(value);
return itkImage;
}
/**
* Create a test image with a single pixel value. The image size is determined by the input parameter.
*
* @param value the pixel value the created image is filled with
* @param size the number of voxels in each dimension
*/
template <typename TPixel, unsigned int VDimension>
typename itk::Image<TPixel, VDimension>::Pointer CreateTestImageFixedValue(size_t size, TPixel value)
{
typedef typename itk::Image<TPixel, VDimension> ImageType;
typedef typename ImageType::Pointer ImagePointer;
typename ImageType::RegionType imageRegion;
typename ImageType::RegionType::SizeType regionSize;
regionSize.Fill(size);
typename ImageType::RegionType::IndexType regionIndex;
regionIndex.Fill(0);
imageRegion.SetSize(regionSize);
imageRegion.SetIndex(regionIndex);
typename ImageType::SpacingType imageSpacing;
imageSpacing.Fill(1.0f);
typename ImageType::PointType imageOrigin;
imageOrigin.Fill(0.0f);
ImagePointer itkImage = ImageType::New();
itkImage->SetRegions(imageRegion);
itkImage->SetOrigin(imageOrigin);
itkImage->SetSpacing(imageSpacing);
itkImage->Allocate();
itkImage->FillBuffer(value);
return itkImage;
}
/**
* Compares the meta information of both given images for equality.
*/
template <typename ImageType>
bool Assert_ImageMetaData_AreEqual(typename ImageType::Pointer itkImage, mitk::Image::Pointer mitkImage)
{
bool return_value = true;
typename ImageType::RegionType itkRegion = itkImage->GetLargestPossibleRegion();
typename ImageType::SizeType itkImageSize = itkRegion.GetSize();
// check dimension
for (unsigned int idx = 0; idx < mitkImage->GetDimension(); idx++)
{
return_value &= (itkImageSize[idx] == mitkImage->GetDimension(idx));
}
MITK_TEST_CONDITION(return_value, " - Dimensions equal!")
// check pixel type
bool ptype_compare = (mitkImage->GetPixelType() == mitk::MakePixelType<ImageType>());
return_value &= ptype_compare;
MITK_TEST_CONDITION(ptype_compare, " - Pixel types equal!")
mitk::BaseGeometry *imageGeometry = mitkImage->GetGeometry();
const mitk::Point3D origin = imageGeometry->GetOrigin();
bool origin_compare = true;
for (unsigned int idx = 0; idx < 3; idx++)
{
origin_compare &= (itkImage->GetOrigin()[idx] == origin[idx]);
}
return_value &= origin_compare;
MITK_TEST_CONDITION(origin_compare, " - Origin equals!")
return return_value;
}
/**
* Generates a random itk image and imports it to mitk image through ImportItkImage and compares the values
* voxel-wise afterwards
*/
template <typename TPixel, unsigned int VDimension>
void Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue()
{
std::stringstream msg;
msg << "Current type: (Random Image, " << VDimension << "D):" << typeid(TPixel).name() << "\n";
std::cout << msg.str();
bool assert_value = true;
typedef typename itk::Image<TPixel, VDimension> ImageType;
typedef typename ImageType::Pointer ImagePointer;
ImagePointer itkImage = CreateTestImageRandom<TPixel, VDimension>(5);
mitk::Image::Pointer output_import = mitk::ImportItkImage(itkImage);
itk::ImageRegionConstIteratorWithIndex<ImageType> iter(itkImage, itkImage->GetLargestPossibleRegion());
iter.GoToBegin();
mitk::ImagePixelReadAccessor<TPixel, VDimension> readAccessor(output_import);
bool difference = false;
while (!iter.IsAtEnd())
{
TPixel ref = iter.Get();
TPixel val = readAccessor.GetPixelByIndex(iter.GetIndex());
difference |= (ref != val);
if (difference)
{
std::cout << iter.GetIndex() << ":" << ref << " ? " << val << "\n";
}
++iter;
}
assert_value = Assert_ImageMetaData_AreEqual<ImageType>(itkImage, output_import);
MITK_TEST_CONDITION(assert_value && (!difference), "Pixel values are same in voxel-wise comparison.");
}
/**
* Generates an itk image with fixed pixel value and imports it to mitk image through ImportItkImage
* and compares the values voxel-wise afterwards
*/
template <typename TPixel, unsigned int VDimension>
void Assert_ItkImageImportSucceded_ReturnsTrue()
{
std::stringstream msg;
msg << "Current type: " << VDimension << "D):" << typeid(TPixel).name() << "\n";
std::cout << msg.str();
bool assert_value = true;
typedef typename itk::Image<TPixel, VDimension> ImageType;
typedef typename ImageType::Pointer ImagePointer;
ImagePointer itkImage = CreateTestImageFixedValue<TPixel, VDimension>(5, itk::NumericTraits<TPixel>::min());
mitk::Image::Pointer output_import = mitk::ImportItkImage(itkImage);
itk::ImageRegionConstIteratorWithIndex<ImageType> iter(itkImage, itkImage->GetLargestPossibleRegion());
iter.GoToBegin();
mitk::ImagePixelReadAccessor<TPixel, VDimension> readAccessor(output_import);
bool difference = false;
while (!iter.IsAtEnd())
{
TPixel ref = iter.Get();
TPixel val = readAccessor.GetPixelByIndex(iter.GetIndex());
difference |= (ref != val);
if (difference)
{
std::cout << iter.GetIndex() << ":" << ref << " ? " << val << "\n";
}
++iter;
}
assert_value = Assert_ImageMetaData_AreEqual<ImageType>(itkImage, output_import);
MITK_TEST_CONDITION(assert_value && (!difference), "Pixel values are same in voxel-wise comparison.");
}
void Assert_ItkVectorImageImportAndCast_ReturnsTrue()
{
typedef itk::VectorImage<short, 3> ImageType;
ImageType::PixelType value;
value.SetSize(2);
value.SetElement(0, 1);
value.SetElement(0, 2);
ImageType::Pointer itkImage = CreateTestVectorImageFixedValue<short, 3>(5, value);
mitk::Image::Pointer mitkImage = mitk::ImportItkImage(itkImage);
mitk::PixelType pixelType = mitkImage->GetPixelType();
- MITK_TEST_CONDITION(pixelType.GetPixelType() == itk::ImageIOBase::VECTOR, "Vector image pixel type")
- MITK_TEST_CONDITION(pixelType.GetComponentType() == itk::ImageIOBase::SHORT, "Vector image component type")
+ MITK_TEST_CONDITION(pixelType.GetPixelType() == itk::IOPixelEnum::VECTOR, "Vector image pixel type")
+ MITK_TEST_CONDITION(pixelType.GetComponentType() == itk::IOComponentEnum::SHORT, "Vector image component type")
mitk::Image::Pointer mitkImage2;
mitk::CastToMitkImage(itkImage, mitkImage2);
mitk::PixelType pixelType2 = mitkImage2->GetPixelType();
MITK_TEST_CONDITION(pixelType == pixelType2, "ImportItkImage and CastToMitkImage produce same pixel types")
ImageType::Pointer itkImageOut;
mitk::CastToItkImage(mitkImage, itkImageOut);
MITK_TEST_CONDITION(pixelType == mitk::MakePixelType<ImageType>(2), "MITK pixel type equals ITK pixel type")
typedef itk::VectorImage<int, 3> IntImageType;
IntImageType::Pointer itkIntImageOut;
mitk::CastToItkImage(mitkImage, itkIntImageOut);
MITK_TEST_CONDITION(!(pixelType == mitk::MakePixelType<IntImageType>(2)), "MITK pixel type != ITK pixel type")
mitk::Image::Pointer mitkImage3;
mitk::CastToMitkImage(itkImageOut, mitkImage3);
MITK_ASSERT_EQUAL(mitkImage, mitkImage3, "Equality for vector images");
}
int mitkImportItkImageTest(int /*argc*/, char * /*argv*/ [])
{
MITK_TEST_BEGIN("mitkImportItkImageTest")
Assert_ItkImageImportSucceded_ReturnsTrue<short, 3>(); // "Import succesfull on 3D short");
Assert_ItkImageImportSucceded_ReturnsTrue<float, 3>(); // "Import succesfull on float");
Assert_ItkImageImportSucceded_ReturnsTrue<unsigned char, 3>(); // "Import succesfull on uchar");
Assert_ItkImageImportSucceded_ReturnsTrue<int, 3>(); // "Import succesfull on int");
Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue<short, 3>(); // "Import succesfull on 3D short");
Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue<float, 3>(); // "Import succesfull on float");
Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue<unsigned char, 3>(); // "Import succesfull on uchar");
Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue<int, 3>(); // "Import succesfull on int");
Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue<short, 4>(); // "Import succesfull on 3D short");
Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue<float, 4>(); // "Import succesfull on float");
Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue<unsigned char, 4>(); // "Import succesfull on uchar");
Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue<int, 4>(); // "Import succesfull on int");
Assert_ItkVectorImageImportAndCast_ReturnsTrue();
MITK_TEST_END()
}
diff --git a/Modules/Core/test/mitkItkImageIOTest.cpp b/Modules/Core/test/mitkItkImageIOTest.cpp
index 1f608cc5f2..a9d8d6d9eb 100644
--- a/Modules/Core/test/mitkItkImageIOTest.cpp
+++ b/Modules/Core/test/mitkItkImageIOTest.cpp
@@ -1,427 +1,428 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkException.h"
#include <mitkStandardFileLocations.h>
#include <mitkTestFixture.h>
#include <mitkTestingMacros.h>
#include "mitkIOUtil.h"
+#include <mitkUtf8Util.h>
#include "mitkITKImageImport.h"
#include <mitkExtractSliceFilter.h>
#include "itksys/SystemTools.hxx"
#include <itkImageRegionIterator.h>
#include <fstream>
#include <iostream>
#ifdef WIN32
#include "process.h"
#else
#include <unistd.h>
#endif
class mitkItkImageIOTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkItkImageIOTestSuite);
MITK_TEST(TestImageWriterJpg);
MITK_TEST(TestImageWriterPng1);
MITK_TEST(TestImageWriterPng2);
MITK_TEST(TestImageWriterPng3);
MITK_TEST(TestImageWriterSimple);
MITK_TEST(TestWrite3DImageWithOnePlane);
MITK_TEST(TestWrite3DImageWithTwoPlanes);
MITK_TEST(TestWrite3DplusT_ArbitraryTG);
MITK_TEST(TestWrite3DplusT_ProportionalTG);
CPPUNIT_TEST_SUITE_END();
public:
void setUp() override {}
void tearDown() override {}
void TestImageWriterJpg() { TestImageWriter("NrrdWritingTestImage.jpg"); }
void TestImageWriterPng1() { TestImageWriter("Png2D-bw.png"); }
void TestImageWriterPng2() { TestImageWriter("RenderingTestData/rgbImage.png"); }
void TestImageWriterPng3() { TestImageWriter("RenderingTestData/rgbaImage.png"); }
void TestWrite3DplusT_ArbitraryTG()
{
TestImageWriter("3D+t-ITKIO-TestData/LinearModel_4D_arbitrary_time_geometry.nrrd");
}
void TestWrite3DplusT_ProportionalTG()
{
TestImageWriter("3D+t-ITKIO-TestData/LinearModel_4D_prop_time_geometry.nrrd");
}
void TestImageWriterSimple()
{
// TODO
}
std::string AppendExtension(const std::string &filename, const char *extension)
{
std::string new_filename = filename;
new_filename += extension;
return new_filename;
}
bool CompareImageMetaData(mitk::Image::Pointer image, mitk::Image::Pointer reference, bool checkPixelType = true)
{
// switch to AreIdentical() methods as soon as Bug 11925 (Basic comparison operators) is fixed
if (image->GetDimension() != reference->GetDimension())
{
MITK_ERROR << "The image dimension differs: IN (" << image->GetDimension() << ") REF("
<< reference->GetDimension() << ")";
return false;
}
// pixel type
if (checkPixelType &&
(image->GetPixelType() != reference->GetPixelType() &&
image->GetPixelType().GetBitsPerComponent() != reference->GetPixelType().GetBitsPerComponent()))
{
MITK_ERROR << "Pixeltype differs ( image=" << image->GetPixelType().GetPixelTypeAsString() << "["
<< image->GetPixelType().GetBitsPerComponent() << "]"
<< " reference=" << reference->GetPixelType().GetPixelTypeAsString() << "["
<< reference->GetPixelType().GetBitsPerComponent() << "]"
<< " )";
return false;
}
return true;
}
/*
Test writing picture formats like *.bmp, *.png, *.tiff or *.jpg
NOTE: Saving as picture format must ignore PixelType comparison - not all bits per components are supported (see
specification of the format)
*/
void TestPictureWriting(mitk::Image *image, const std::string &filename, const std::string &extension)
{
const std::string fullFileName = AppendExtension(filename, extension.c_str());
mitk::Image::Pointer singleSliceImage = nullptr;
if (image->GetDimension() == 3)
{
mitk::ExtractSliceFilter::Pointer extractFilter = mitk::ExtractSliceFilter::New();
extractFilter->SetInput(image);
extractFilter->SetWorldGeometry(image->GetSlicedGeometry()->GetPlaneGeometry(0));
extractFilter->Update();
singleSliceImage = extractFilter->GetOutput();
// test 3D writing in format supporting only 2D
mitk::IOUtil::Save(image, fullFileName);
// test images
unsigned int foundImagesCount = 0;
// if the image only contains one sinlge slice the itkImageSeriesWriter won't add a number like
// filename.XX.extension
if (image->GetDimension(2) == 1)
{
std::stringstream series_filenames;
series_filenames << filename << extension;
mitk::Image::Pointer compareImage = mitk::IOUtil::Load<mitk::Image>(series_filenames.str());
if (compareImage.IsNotNull())
{
foundImagesCount++;
MITK_TEST_CONDITION(
CompareImageMetaData(singleSliceImage, compareImage, false),
"Image meta data unchanged after writing and loading again. "); // ignore bits per component
}
remove(series_filenames.str().c_str());
}
else // test the whole slice stack
{
for (unsigned int i = 0; i < image->GetDimension(2); i++)
{
std::stringstream series_filenames;
series_filenames << filename << "." << i + 1 << extension;
mitk::Image::Pointer compareImage = mitk::IOUtil::Load<mitk::Image>(series_filenames.str());
if (compareImage.IsNotNull())
{
foundImagesCount++;
MITK_TEST_CONDITION(
CompareImageMetaData(singleSliceImage, compareImage, false),
"Image meta data unchanged after writing and loading again. "); // ignore bits per component
}
remove(series_filenames.str().c_str());
}
}
MITK_TEST_CONDITION(foundImagesCount == image->GetDimension(2),
"All 2D-Slices of a 3D image were stored correctly.");
}
else if (image->GetDimension() == 2)
{
singleSliceImage = image;
}
// test 2D writing
if (singleSliceImage.IsNotNull())
{
try
{
mitk::IOUtil::Save(singleSliceImage, fullFileName);
mitk::Image::Pointer compareImage = mitk::IOUtil::Load<mitk::Image>(fullFileName.c_str());
MITK_TEST_CONDITION_REQUIRED(compareImage.IsNotNull(), "Image stored was succesfully loaded again");
MITK_TEST_CONDITION_REQUIRED(
CompareImageMetaData(singleSliceImage, compareImage, false),
"Image meta data unchanged after writing and loading again. "); // ignore bits per component
remove(fullFileName.c_str());
}
catch (itk::ExceptionObject &e)
{
MITK_TEST_FAILED_MSG(<< "Exception during file writing for ." << extension << ": " << e.what());
}
}
}
/**
* test for writing NRRDs
*/
void TestNRRDWriting(const mitk::Image *image)
{
CPPUNIT_ASSERT_MESSAGE("Internal error. Passed reference image is null.", image);
std::ofstream tmpStream;
std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.nrrd");
tmpStream.close();
try
{
mitk::IOUtil::Save(image, tmpFilePath);
mitk::Image::Pointer compareImage = mitk::IOUtil::Load<mitk::Image>(tmpFilePath);
CPPUNIT_ASSERT_MESSAGE("Image stored in NRRD format was succesfully loaded again", compareImage.IsNotNull());
/*It would make sence to check the images as well (see commented cppunit assert),
but currently there seems to be a problem (exception) with most of the test images
(partly it seems to be a problem when try to access the pixel content by AccessByItk_1
in mitk::CompareImageDataFilter.
This problem should be dealt with in Bug 19533 - mitkITKImageIOTest needs improvement */
// CPPUNIT_ASSERT_MESSAGE("Images are equal.", mitk::Equal(*image, *compareImage, mitk::eps, true));
CPPUNIT_ASSERT_MESSAGE(
"TimeGeometries are equal.",
mitk::Equal(*(image->GetTimeGeometry()), *(compareImage->GetTimeGeometry()), mitk::eps, true));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error, read image has different UID", image->GetUID(), compareImage->GetUID());
remove(tmpFilePath.c_str());
}
catch (...)
{
std::remove(tmpFilePath.c_str());
CPPUNIT_FAIL("Exception during NRRD file writing");
}
}
/**
* test for writing MHDs
*/
void TestMHDWriting(const mitk::Image *image)
{
CPPUNIT_ASSERT_MESSAGE("Internal error. Passed reference image is null.", image);
std::ofstream tmpStream;
std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.mhd");
tmpStream.close();
std::string tmpFilePathWithoutExt = tmpFilePath.substr(0, tmpFilePath.size() - 4);
try
{
mitk::IOUtil::Save(image, tmpFilePath);
mitk::Image::Pointer compareImage = mitk::IOUtil::Load<mitk::Image>(tmpFilePath);
CPPUNIT_ASSERT_MESSAGE("Image stored in MHD format was succesfully loaded again! ", compareImage.IsNotNull());
CPPUNIT_ASSERT_MESSAGE(".mhd file exists",
- itksys::SystemTools::FileExists((tmpFilePathWithoutExt + ".mhd").c_str()));
+ itksys::SystemTools::FileExists(mitk::Utf8Util::Local8BitToUtf8(tmpFilePathWithoutExt + ".mhd").c_str()));
CPPUNIT_ASSERT_MESSAGE(".raw or .zraw exists",
- itksys::SystemTools::FileExists((tmpFilePathWithoutExt + ".raw").c_str()) ||
- itksys::SystemTools::FileExists((tmpFilePathWithoutExt + ".zraw").c_str()));
+ itksys::SystemTools::FileExists(mitk::Utf8Util::Local8BitToUtf8(tmpFilePathWithoutExt + ".raw").c_str()) ||
+ itksys::SystemTools::FileExists(mitk::Utf8Util::Local8BitToUtf8(tmpFilePathWithoutExt + ".zraw").c_str()));
/*It would make sence to check the images as well (see commented cppunit assert),
but currently there seems to be a problem (exception) with most of the test images
(partly it seems to be a problem when try to access the pixel content by AccessByItk_1
in mitk::CompareImageDataFilter.
This problem should be dealt with in Bug 19533 - mitkITKImageIOTest needs improvement */
// CPPUNIT_ASSERT_MESSAGE("Images are equal.", mitk::Equal(*image, *compareImage, mitk::eps, true));
CPPUNIT_ASSERT_MESSAGE("TimeGeometries are equal.",
mitk::Equal(*(image->GetTimeGeometry()), *(compareImage->GetTimeGeometry()), 5e-4, true));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error, read image has different UID", image->GetUID(), compareImage->GetUID());
// delete
remove(tmpFilePath.c_str());
remove((tmpFilePathWithoutExt + ".raw").c_str());
remove((tmpFilePathWithoutExt + ".zraw").c_str());
}
catch (...)
{
CPPUNIT_FAIL("Exception during.mhd file writing");
}
}
/**
* test for "ImageWriter".
*
* argc and argv are the command line parameters which were passed to
* the ADD_TEST command in the CMakeLists.txt file. For the automatic
* tests, argv is either empty for the simple tests or contains the filename
* of a test image for the image tests (see CMakeLists.txt).
*/
void TestImageWriter(std::string sourcefile)
{
sourcefile = GetTestDataFilePath(sourcefile);
// load image
- CPPUNIT_ASSERT_MESSAGE("Checking whether source image exists", itksys::SystemTools::FileExists(sourcefile.c_str()));
+ CPPUNIT_ASSERT_MESSAGE("Checking whether source image exists", itksys::SystemTools::FileExists(mitk::Utf8Util::Local8BitToUtf8(sourcefile).c_str()));
mitk::Image::Pointer image = nullptr;
try
{
image = mitk::IOUtil::Load<mitk::Image>(sourcefile);
}
catch (...)
{
CPPUNIT_FAIL("Exception during file loading:");
}
CPPUNIT_ASSERT_MESSAGE("loaded image not nullptr", image.IsNotNull());
// write ITK .mhd image (2D and 3D only)
if (image->GetDimension() <= 3)
{
TestMHDWriting(image);
}
// testing more component image writing as nrrd files
TestNRRDWriting(image);
std::ofstream tmpStream;
std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX");
tmpStream.close();
TestPictureWriting(image, tmpFilePath, ".png");
TestPictureWriting(image, tmpFilePath, ".jpg");
TestPictureWriting(image, tmpFilePath, ".tiff");
TestPictureWriting(image, tmpFilePath, ".bmp");
// always end with this!
}
/**
* Try to write a 3D image with only one plane (a 2D images in disguise for all intents and purposes)
*/
void TestWrite3DImageWithOnePlane()
{
typedef itk::Image<unsigned char, 3> ImageType;
ImageType::Pointer itkImage = ImageType::New();
ImageType::IndexType start;
start.Fill(0);
ImageType::SizeType size;
size[0] = 100;
size[1] = 100;
size[2] = 1;
ImageType::RegionType region;
region.SetSize(size);
region.SetIndex(start);
itkImage->SetRegions(region);
itkImage->Allocate();
itkImage->FillBuffer(0);
itk::ImageRegionIterator<ImageType> imageIterator(itkImage, itkImage->GetLargestPossibleRegion());
// Make two squares
while (!imageIterator.IsAtEnd())
{
if ((imageIterator.GetIndex()[0] > 5 && imageIterator.GetIndex()[0] < 20) &&
(imageIterator.GetIndex()[1] > 5 && imageIterator.GetIndex()[1] < 20))
{
imageIterator.Set(255);
}
if ((imageIterator.GetIndex()[0] > 50 && imageIterator.GetIndex()[0] < 70) &&
(imageIterator.GetIndex()[1] > 50 && imageIterator.GetIndex()[1] < 70))
{
imageIterator.Set(60);
}
++imageIterator;
}
mitk::Image::Pointer image = mitk::ImportItkImage(itkImage);
mitk::IOUtil::Save(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.nrrd"));
mitk::IOUtil::Save(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.png"));
}
/**
* Try to write a 3D image with only one plane (a 2D images in disguise for all intents and purposes)
*/
void TestWrite3DImageWithTwoPlanes()
{
typedef itk::Image<unsigned char, 3> ImageType;
ImageType::Pointer itkImage = ImageType::New();
ImageType::IndexType start;
start.Fill(0);
ImageType::SizeType size;
size[0] = 100;
size[1] = 100;
size[2] = 2;
ImageType::RegionType region;
region.SetSize(size);
region.SetIndex(start);
itkImage->SetRegions(region);
itkImage->Allocate();
itkImage->FillBuffer(0);
itk::ImageRegionIterator<ImageType> imageIterator(itkImage, itkImage->GetLargestPossibleRegion());
// Make two squares
while (!imageIterator.IsAtEnd())
{
if ((imageIterator.GetIndex()[0] > 5 && imageIterator.GetIndex()[0] < 20) &&
(imageIterator.GetIndex()[1] > 5 && imageIterator.GetIndex()[1] < 20))
{
imageIterator.Set(255);
}
if ((imageIterator.GetIndex()[0] > 50 && imageIterator.GetIndex()[0] < 70) &&
(imageIterator.GetIndex()[1] > 50 && imageIterator.GetIndex()[1] < 70))
{
imageIterator.Set(60);
}
++imageIterator;
}
mitk::Image::Pointer image = mitk::ImportItkImage(itkImage);
mitk::IOUtil::Save(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.nrrd"));
CPPUNIT_ASSERT_THROW(mitk::IOUtil::Save(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.png")),
mitk::Exception);
}
};
MITK_TEST_SUITE_REGISTRATION(mitkItkImageIO)
diff --git a/Modules/Core/test/mitkLogTest.cpp b/Modules/Core/test/mitkLogTest.cpp
index 119855e481..716d93c6cf 100644
--- a/Modules/Core/test/mitkLogTest.cpp
+++ b/Modules/Core/test/mitkLogTest.cpp
@@ -1,312 +1,300 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkCommon.h"
#include "mitkTestingMacros.h"
-#include <itkMultiThreader.h>
#include <itksys/SystemTools.hxx>
#include <mitkLog.h>
#include <mitkNumericTypes.h>
#include <mitkStandardFileLocations.h>
+#include <thread>
+#include <mitkUtf8Util.h>
/** Documentation
*
* @brief this class provides an accessible BackendCout to determine whether this backend was
* used to process a message or not.
* It is needed for the disable / enable backend test.
*/
class TestBackendCout : public mbilog::BackendCout
{
public:
TestBackendCout()
{
m_Called = false;
mbilog::BackendCout();
}
void ProcessMessage(const mbilog::LogMessage &l) override
{
m_Called = true;
mbilog::BackendCout::ProcessMessage(l);
}
bool WasCalled() { return m_Called; }
private:
bool m_Called;
};
/** Documentation
*
* @brief Objects of this class can start an internal thread by calling the Start() method.
* The thread is then logging messages until the method Stop() is called. The class
* can be used to test if logging is thread-save by using multiple objects and let
* them log simuntanously.
*/
class mitkTestLoggingThread : public itk::Object
{
public:
mitkClassMacroItkParent(mitkTestLoggingThread, itk::Object);
- mitkNewMacro1Param(mitkTestLoggingThread, itk::MultiThreader::Pointer);
+ itkFactorylessNewMacro(Self);
int NumberOfMessages;
protected:
- mitkTestLoggingThread(itk::MultiThreader::Pointer MultiThreader)
+ mitkTestLoggingThread()
+ : NumberOfMessages(0),
+ LoggingRunning(true)
{
- ThreadID = -1;
- NumberOfMessages = 0;
- m_MultiThreader = MultiThreader;
- LoggingRunning = true;
}
- bool LoggingRunning;
-
- int ThreadID;
+ ~mitkTestLoggingThread()
+ {
+ this->Stop();
+ }
- itk::MultiThreader::Pointer m_MultiThreader;
+ bool LoggingRunning;
+ std::thread Thread;
void LogMessages()
{
+ auto ThreadID = Thread.get_id();
+
while (LoggingRunning)
{
MITK_INFO << "Test info stream in thread" << ThreadID << "\n even with newlines";
MITK_WARN << "Test warning stream in thread " << ThreadID << ". "
<< "Even with a very long text, even without meaning or implied meaning or content, just a long "
"sentence to see whether something has problems with long sentences or output in files or into "
"windows or commandlines or whatever.";
MITK_DEBUG << "Test debugging stream in thread " << ThreadID;
MITK_ERROR << "Test error stream in thread " << ThreadID;
MITK_FATAL << "Test fatal stream in thread " << ThreadID;
NumberOfMessages += 5;
}
}
- static ITK_THREAD_RETURN_TYPE ThreadStartTracking(void *pInfoStruct)
+ static void ThreadStartTracking(void *instance)
{
- /* extract this pointer from Thread Info structure */
- auto *pInfo = (struct itk::MultiThreader::ThreadInfoStruct *)pInfoStruct;
- if (pInfo == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
- if (pInfo->UserData == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
- auto *thisthread = (mitkTestLoggingThread *)pInfo->UserData;
+ auto* thisthread = reinterpret_cast<mitkTestLoggingThread*>(instance);
if (thisthread != nullptr)
thisthread->LogMessages();
-
- return ITK_THREAD_RETURN_VALUE;
}
public:
- int Start()
+ std::thread::id Start()
{
LoggingRunning = true;
- this->ThreadID = m_MultiThreader->SpawnThread(this->ThreadStartTracking, this);
- return ThreadID;
+ Thread = std::thread(this->ThreadStartTracking, this);
+ return Thread.get_id();
}
- void Stop() { LoggingRunning = false; }
+ void Stop()
+ {
+ LoggingRunning = false;
+
+ if(Thread.joinable())
+ Thread.join();
+ }
};
/** Documentation
*
* @brief This class holds static test methods to sturcture the test of the mitk logging mechanism.
*/
class mitkLogTestClass
{
public:
static void TestSimpleLog()
{
bool testSucceded = true;
try
{
MITK_INFO << "Test info stream.";
MITK_WARN << "Test warning stream.";
MITK_DEBUG << "Test debugging stream."; // only activated if cmake variable is on!
// so no worries if you see no output for this line
MITK_ERROR << "Test error stream.";
MITK_FATAL << "Test fatal stream.";
}
catch (const mitk::Exception &)
{
testSucceded = false;
}
MITK_TEST_CONDITION_REQUIRED(testSucceded, "Test logging streams.");
}
static void TestObjectInfoLogging()
{
bool testSucceded = true;
try
{
int i = 123;
float f = .32234;
double d = 123123;
std::string testString = "testString";
std::stringstream testStringStream;
testStringStream << "test"
<< "String"
<< "Stream";
mitk::Point3D testMitkPoint;
testMitkPoint.Fill(2);
MITK_INFO << i;
MITK_INFO << f;
MITK_INFO << d;
MITK_INFO << testString;
MITK_INFO << testStringStream.str();
MITK_INFO << testMitkPoint;
}
catch (const mitk::Exception &)
{
testSucceded = false;
}
MITK_TEST_CONDITION_REQUIRED(testSucceded, "Test logging of object information.");
}
static void TestThreadSaveLog(bool toFile)
{
bool testSucceded = true;
try
{
if (toFile)
{
std::string filename = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + "/testthreadlog.log";
- itksys::SystemTools::RemoveFile(filename.c_str()); // remove old file, we do not want to append to large files
+ itksys::SystemTools::RemoveFile(mitk::Utf8Util::Local8BitToUtf8(filename).c_str()); // remove old file, we do not want to append to large files
mitk::LoggingBackend::SetLogFile(filename.c_str());
}
unsigned int numberOfThreads = 20;
unsigned int threadRuntimeInMilliseconds = 2000;
- std::vector<unsigned int> threadIDs;
+ std::vector<std::thread::id> threadIDs;
std::vector<mitkTestLoggingThread::Pointer> threads;
- itk::MultiThreader::Pointer multiThreader = itk::MultiThreader::New();
for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx)
{
// initialize threads...
- mitkTestLoggingThread::Pointer newThread = mitkTestLoggingThread::New(multiThreader);
- threads.push_back(newThread);
+ threads.push_back(mitkTestLoggingThread::New());
std::cout << "Created " << threadIdx << ". thread." << std::endl;
}
for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx)
{
// start them
std::cout << "Start " << threadIdx << ". thread." << std::endl;
threadIDs.push_back(threads[threadIdx]->Start());
std::cout << threadIdx << ". thread has ID " << threadIDs[threadIdx] << std::endl;
}
// wait for some time (milliseconds)
itksys::SystemTools::Delay(threadRuntimeInMilliseconds);
for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx)
{
- // stop them
+ // stop them and wait for them to end
std::cout << "Stop " << threadIdx << ". thread." << std::endl;
threads[threadIdx]->Stop();
- }
-
- for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx)
- {
- // Wait for all threads to end
- multiThreader->TerminateThread(threadIDs[threadIdx]);
- std::cout << "Terminated " << threadIdx << ". thread (" << threads[threadIdx]->NumberOfMessages << " messages)."
- << std::endl;
+ std::cout << "Terminated " << threadIdx << ". thread (" << threads[threadIdx]->NumberOfMessages << " messages)." << std::endl;
}
}
catch (std::exception &e)
{
MITK_ERROR << "exception during 'TestThreadSaveLog': " << e.what();
testSucceded = false;
}
catch (...)
{
MITK_ERROR << "unknown exception during 'TestThreadSaveLog'";
testSucceded = false;
}
// if no error occured until now, everything is ok
MITK_TEST_CONDITION_REQUIRED(testSucceded, "Test logging in different threads.");
}
static void TestLoggingToFile()
{
std::string filename = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + "/testlog.log";
mitk::LoggingBackend::SetLogFile(filename.c_str());
MITK_INFO << "Test logging to default filename: " << mitk::LoggingBackend::GetLogFile();
- MITK_TEST_CONDITION_REQUIRED(itksys::SystemTools::FileExists(filename.c_str()), "Testing if log file exists.");
+ MITK_TEST_CONDITION_REQUIRED(itksys::SystemTools::FileExists(mitk::Utf8Util::Local8BitToUtf8(filename).c_str()), "Testing if log file exists.");
// TODO delete log file?
}
static void TestAddAndRemoveBackends()
{
mbilog::BackendCout myBackend = mbilog::BackendCout();
mbilog::RegisterBackend(&myBackend);
MITK_INFO << "Test logging";
mbilog::UnregisterBackend(&myBackend);
// if no error occured until now, everything is ok
MITK_TEST_CONDITION_REQUIRED(true, "Test add/remove logging backend.");
}
static void TestDefaultBackend()
{
// not possible now, because we cannot unregister the mitk logging backend in the moment. If such a method is added
// to mbilog utility one may add this test.
}
static void TestEnableDisableBackends()
{
TestBackendCout myCoutBackend = TestBackendCout();
mbilog::RegisterBackend(&myCoutBackend);
mbilog::DisableBackends(mbilog::Console);
MITK_INFO << "There should be no output!";
bool success = !myCoutBackend.WasCalled();
mbilog::EnableBackends(mbilog::Console);
MITK_INFO << "Now there should be an output.";
success &= myCoutBackend.WasCalled();
mbilog::UnregisterBackend(&myCoutBackend);
MITK_TEST_CONDITION_REQUIRED(success, "Test disable / enable logging backends.")
}
};
int mitkLogTest(int /* argc */, char * /*argv*/ [])
{
// always start with this!
MITK_TEST_BEGIN("Log")
MITK_TEST_OUTPUT(<< "TESTING ALL LOGGING OUTPUTS, ERROR MESSAGES ARE ALSO TESTED AND NOT MEANING AN ERROR OCCURED!")
mitkLogTestClass::TestSimpleLog();
mitkLogTestClass::TestObjectInfoLogging();
mitkLogTestClass::TestLoggingToFile();
mitkLogTestClass::TestAddAndRemoveBackends();
mitkLogTestClass::TestThreadSaveLog(false); // false = to console
mitkLogTestClass::TestThreadSaveLog(true); // true = to file
mitkLogTestClass::TestEnableDisableBackends();
// TODO actually test file somehow?
// always end with this!
MITK_TEST_END()
}
diff --git a/Modules/Core/test/mitkPixelTypeTest.cpp b/Modules/Core/test/mitkPixelTypeTest.cpp
index 0df8ef5104..3bafd0bff5 100644
--- a/Modules/Core/test/mitkPixelTypeTest.cpp
+++ b/Modules/Core/test/mitkPixelTypeTest.cpp
@@ -1,241 +1,241 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// Testing
#include "mitkTestFixture.h"
#include "mitkTestingMacros.h"
// std includes
#include <string>
// MITK includes
#include "mitkPixelType.h"
#include <mitkLogMacros.h>
// ITK includes
#include "itkImage.h"
#include <itkVectorImage.h>
// VTK includes
#include <vtkDebugLeaks.h>
struct MyObscurePixelType
{
typedef float ValueType;
static const size_t Length = 2;
};
//## Documentation
//## main testing method
class mitkPixelTypeTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkPixelTypeTestSuite);
MITK_TEST(GetComponentType_Success);
MITK_TEST(GetPixelTypeAsString_Success);
MITK_TEST(GetBpePtype_Success);
MITK_TEST(GetNumberOfComponentsPtype_Success);
MITK_TEST(GetBitsPerComponentPtype_Success);
MITK_TEST(GetBpeItkPtype_Success);
MITK_TEST(GetNumberOfComponentsItkPtype_Success);
MITK_TEST(GetBitsPerComponentItkPtype_Success);
MITK_TEST(ConstructorWithBrackets_Success);
MITK_TEST(InitializeWithEqualSign_Success);
MITK_TEST(EqualityOperator_Success);
MITK_TEST(NotEqualityOperator_Success);
MITK_TEST(GetPixelTypeUnknown_Success);
MITK_TEST(SetLengthCorrectly_Success);
MITK_TEST(ValueTypeCorresponds_Success);
MITK_TEST(ImageTypeTraitInt3D_Success);
MITK_TEST(ImageTypeTraitShort2D_Success);
MITK_TEST(ImageTypeTraitVectorShort3D_Success);
MITK_TEST(ImageTypeTraitItkInt3D_Success);
MITK_TEST(ImageTypeTraitItkShort2D_Success);
MITK_TEST(ImageTypeTraitItkVectorShort3D_Success);
CPPUNIT_TEST_SUITE_END();
private:
typedef itk::Image<itk::FixedArray<int, 5>, 3> ItkImageType;
typedef itk::Image<MyObscurePixelType> MyObscureImageType;
typedef itk::VectorImage<short, 3> VectorImageType;
typedef itk::Image<itk::Vector<short, 7>> FixedVectorImageType;
public:
void setUp() override
{
}
void tearDown() override
{
}
void GetComponentType_Success()
{
mitk::PixelType ptype = mitk::MakePixelType<int, int, 5>();
MITK_INFO << "m_Ptype = mitk::MakePixelType<int, int, 5>();";
MITK_INFO
<< "m_ItkPtype = mitk::MakePixelType<ItkImageType>();\n with itk::Image<itk::FixedArray< int, 5>, 3> ItkImageType";
- CPPUNIT_ASSERT_MESSAGE("GetComponentType()", ptype.GetComponentType() == itk::ImageIOBase::INT);
+ CPPUNIT_ASSERT_MESSAGE("GetComponentType()", ptype.GetComponentType() == itk::IOComponentEnum::INT);
}
void GetPixelTypeAsString_Success()
{
mitk::PixelType ptype = mitk::MakePixelType<int, int, 5>();
MITK_INFO << ptype.GetPixelTypeAsString();
MITK_INFO << typeid(ItkImageType).name();
}
void GetBpePtype_Success()
{
mitk::PixelType ptype = mitk::MakePixelType<int, int, 5>();
CPPUNIT_ASSERT_MESSAGE("[m_Ptype] GetBpe()", ptype.GetBpe() == 8 * sizeof(int) * 5);
}
void GetNumberOfComponentsPtype_Success()
{
mitk::PixelType ptype = mitk::MakePixelType<int, int, 5>();
CPPUNIT_ASSERT_MESSAGE("[ptype]GetNumberOfComponents()", ptype.GetNumberOfComponents() == 5);
}
void GetBitsPerComponentPtype_Success()
{
mitk::PixelType ptype = mitk::MakePixelType<int, int, 5>();
CPPUNIT_ASSERT_MESSAGE("[ptype]GetBitsPerComponent()", ptype.GetBitsPerComponent() == 8 * sizeof(int));
}
void GetBpeItkPtype_Success()
{
mitk::PixelType itkPtype = mitk::MakePixelType<ItkImageType>();
CPPUNIT_ASSERT_MESSAGE("[itkPType] GetBpe()", itkPtype.GetBpe() == 8 * sizeof(int) * 5);
}
void GetNumberOfComponentsItkPtype_Success()
{
mitk::PixelType itkPtype = mitk::MakePixelType<ItkImageType>();
CPPUNIT_ASSERT_MESSAGE("[itkPType] GetNumberOfComponents()", itkPtype.GetNumberOfComponents() == 5);
}
void GetBitsPerComponentItkPtype_Success()
{
mitk::PixelType itkPtype = mitk::MakePixelType<ItkImageType>();
CPPUNIT_ASSERT_MESSAGE("[itkPType] GetBitsPerComponent()", itkPtype.GetBitsPerComponent() == 8 * sizeof(int));
}
void ConstructorWithBrackets_Success()
{
mitk::PixelType ptype = mitk::MakePixelType<int, int, 5>();
mitk::PixelType ptype2(ptype);
- CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)- GetComponentType()", ptype2.GetComponentType() == itk::ImageIOBase::INT);
+ CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)- GetComponentType()", ptype2.GetComponentType() == itk::IOComponentEnum::INT);
CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetPixelType(", ptype2.GetPixelType() == ptype.GetPixelType());
CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetComponentType(", ptype2.GetComponentType() == ptype.GetComponentType());
CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetBpe()", ptype2.GetBpe() == 8 * sizeof(int) * 5);
CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetNumberOfComponents()", ptype2.GetNumberOfComponents() == 5);
CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetBitsPerComponent()", ptype2.GetBitsPerComponent() == 8 * sizeof(int));
}
void InitializeWithEqualSign_Success()
{
mitk::PixelType ptype = mitk::MakePixelType<int, int, 5>();
mitk::PixelType ptype2 = ptype;
- CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetComponentType()", ptype2.GetComponentType() == itk::ImageIOBase::INT);
+ CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetComponentType()", ptype2.GetComponentType() == itk::IOComponentEnum::INT);
CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetPixelType(", ptype2.GetPixelType() == ptype.GetPixelType());
CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetComponentType(", ptype2.GetComponentType() == ptype.GetComponentType());
CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetBpe()", ptype2.GetBpe() == 8 * sizeof(int) * 5);
CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetNumberOfComponents()", ptype2.GetNumberOfComponents() == 5);
CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetBitsPerComponent()", ptype2.GetBitsPerComponent() == 8 * sizeof(int));
}
void EqualityOperator_Success()
{
mitk::PixelType ptype = mitk::MakePixelType<int, int, 5>();
mitk::PixelType ptype2 = ptype;
CPPUNIT_ASSERT_MESSAGE("operator ==", ptype == ptype2);
}
void NotEqualityOperator_Success()
{
mitk::PixelType ptype = mitk::MakePixelType<int, int, 5>();
mitk::PixelType ptype3 = mitk::MakePixelType<char, char ,5>();
CPPUNIT_ASSERT_MESSAGE("operator !=", ptype != ptype3);
}
void GetPixelTypeUnknown_Success()
{
// test instantiation
mitk::PixelType obscurePixelType = mitk::MakePixelType<MyObscureImageType>();
- CPPUNIT_ASSERT_MESSAGE("PixelTypeId is 'UNKNOWN' ", obscurePixelType.GetPixelType() == itk::ImageIOBase::UNKNOWNPIXELTYPE);
+ CPPUNIT_ASSERT_MESSAGE("PixelTypeId is 'UNKNOWN' ", obscurePixelType.GetPixelType() == itk::IOPixelEnum::UNKNOWNPIXELTYPE);
}
void SetLengthCorrectly_Success()
{
mitk::PixelType obscurePixelType = mitk::MakePixelType<MyObscureImageType>();
CPPUNIT_ASSERT_MESSAGE("Lenght was set correctly", obscurePixelType.GetNumberOfComponents() == MyObscurePixelType::Length);
}
void ValueTypeCorresponds_Success()
{
mitk::PixelType obscurePixelType = mitk::MakePixelType<MyObscureImageType>();
CPPUNIT_ASSERT_MESSAGE("ValueType corresponds.",
obscurePixelType.GetComponentType() == mitk::MapPixelComponentType<MyObscurePixelType::ValueType>::value);
}
void ImageTypeTraitInt3D_Success()
{
// test ImageTypeTrait traits class
CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid int 3D equals ITK typeid",
typeid(mitk::ImageTypeTrait<int, 3>::ImageType) == typeid(itk::Image<int, 3>));
CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is no vector image", (mitk::ImageTypeTrait<int, 3>::IsVectorImage == false));
}
void ImageTypeTraitShort2D_Success()
{
CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid short 2D equals ITK typeid",
typeid(mitk::ImageTypeTrait<itk::FixedArray<short, 2>, 3>::ImageType) ==
typeid(itk::Image<itk::FixedArray<short, 2>, 3>));
CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is no vector image", (mitk::ImageTypeTrait<itk::FixedArray<short, 2>, 3>::IsVectorImage == false));
}
void ImageTypeTraitVectorShort3D_Success()
{
CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid short 3D equals ITK typeid",
typeid(mitk::ImageTypeTrait<itk::VariableLengthVector<short>, 3>::ImageType) == typeid(itk::VectorImage<short, 3>));
CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is a vector image", (mitk::ImageTypeTrait<itk::VariableLengthVector<short>, 3>::IsVectorImage == true));
}
void ImageTypeTraitItkInt3D_Success()
{
CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid ITK int 3D equals ITK typeid",
typeid(mitk::ImageTypeTrait<itk::Image<int, 3>>::ImageType) == typeid(itk::Image<int, 3>));
CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is no vector image", (mitk::ImageTypeTrait<itk::Image<int, 3>>::IsVectorImage == false));
}
void ImageTypeTraitItkShort2D_Success()
{
CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid ITK short 2D equals ITK typeid",
typeid(mitk::ImageTypeTrait<itk::Image<itk::FixedArray<short, 2>, 3>>::ImageType) ==
typeid(itk::Image<itk::FixedArray<short, 2>, 3>));
CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is no vector image",
(mitk::ImageTypeTrait<itk::Image<itk::FixedArray<short, 2>, 3>>::IsVectorImage == false));
}
void ImageTypeTraitItkVectorShort3D_Success()
{
CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid ITK short 3D equals ITK typeid",
typeid(mitk::ImageTypeTrait<itk::VectorImage<short, 3>>::ImageType) == typeid(itk::VectorImage<short, 3>));
CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is a vector image",
(mitk::ImageTypeTrait<itk::VectorImage<short, 3>>::IsVectorImage == true));
}
};
MITK_TEST_SUITE_REGISTRATION(mitkPixelType)
diff --git a/Modules/Core/test/mitkPlanePositionManagerTest.cpp b/Modules/Core/test/mitkPlanePositionManagerTest.cpp
index 39197042d5..dbb23f8049 100644
--- a/Modules/Core/test/mitkPlanePositionManagerTest.cpp
+++ b/Modules/Core/test/mitkPlanePositionManagerTest.cpp
@@ -1,273 +1,272 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkBaseProperty.h"
#include "mitkDataNode.h"
#include "mitkGeometry3D.h"
#include "mitkImage.h"
#include "mitkInteractionConst.h"
#include "mitkPlaneGeometry.h"
#include "mitkPlanePositionManager.h"
#include "mitkRotationOperation.h"
#include "mitkSliceNavigationController.h"
#include "mitkStandaloneDataStorage.h"
#include "mitkStringProperty.h"
#include "mitkSurface.h"
#include "mitkTestingMacros.h"
#include "usGetModuleContext.h"
#include "usModuleContext.h"
#include "usServiceReference.h"
#include "vnl/vnl_vector.h"
-#include <itkAffineGeometryFrame.h>
std::vector<mitk::PlaneGeometry::Pointer> m_Geometries;
std::vector<unsigned int> m_SliceIndices;
mitk::PlanePositionManagerService *m_Service;
int SetUpBeforeTest()
{
// Getting Service
us::ServiceReference<mitk::PlanePositionManagerService> serviceRef =
us::GetModuleContext()->GetServiceReference<mitk::PlanePositionManagerService>();
m_Service = us::GetModuleContext()->GetService(serviceRef);
if (m_Service == nullptr)
return EXIT_FAILURE;
// Creating different Geometries
m_Geometries.reserve(100);
mitk::PlaneGeometry::PlaneOrientation views[] = {
mitk::PlaneGeometry::Axial, mitk::PlaneGeometry::Sagittal, mitk::PlaneGeometry::Frontal};
for (unsigned int i = 0; i < 100; ++i)
{
mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New();
mitk::ScalarType width = 256 + (0.01 * i);
mitk::ScalarType height = 256 + (0.002 * i);
mitk::Vector3D right;
mitk::Vector3D down;
right[0] = 1;
right[1] = i;
right[2] = 0.5;
down[0] = i * 0.02;
down[1] = 1;
down[2] = i * 0.03;
mitk::Vector3D spacing;
mitk::FillVector3D(spacing, 1.0 * 0.02 * i, 1.0 * 0.15 * i, 1.0);
mitk::Vector3D rightVector;
mitk::FillVector3D(rightVector, 0.02 * (i + 1), 0 + (0.05 * i), 1.0);
mitk::Vector3D downVector;
mitk::FillVector3D(downVector, 1, 3 - 0.01 * i, 0.0345 * i);
vnl_vector<mitk::ScalarType> normal = vnl_cross_3d(rightVector.GetVnlVector(), downVector.GetVnlVector());
normal.normalize();
normal *= 1.5;
mitk::Vector3D origin;
origin.Fill(1);
origin[0] = 12 + 0.03 * i;
mitk::AffineTransform3D::Pointer transform = mitk::AffineTransform3D::New();
mitk::Matrix3D matrix;
matrix.GetVnlMatrix().set_column(0, rightVector.GetVnlVector());
matrix.GetVnlMatrix().set_column(1, downVector.GetVnlVector());
matrix.GetVnlMatrix().set_column(2, normal);
transform->SetMatrix(matrix);
transform->SetOffset(origin);
plane->InitializeStandardPlane(width, height, transform, views[i % 3], i, true, false);
m_Geometries.push_back(plane);
}
return EXIT_SUCCESS;
}
int testAddPlanePosition()
{
MITK_TEST_OUTPUT(<< "Starting Test: ######### A d d P l a n e P o s i t i o n #########");
MITK_TEST_CONDITION(m_Service != nullptr, "Testing getting of PlanePositionManagerService");
unsigned int currentID(m_Service->AddNewPlanePosition(m_Geometries.at(0), 0));
bool error = ((m_Service->GetNumberOfPlanePositions() != 1) || (currentID != 0));
if (error)
{
MITK_TEST_CONDITION(m_Service->GetNumberOfPlanePositions() == 1, "Checking for correct number of planepositions");
MITK_TEST_CONDITION(currentID == 0, "Testing for correct ID");
return EXIT_FAILURE;
}
// Adding new planes
for (unsigned int i = 1; i < m_Geometries.size(); ++i)
{
unsigned int newID = m_Service->AddNewPlanePosition(m_Geometries.at(i), i);
error = ((m_Service->GetNumberOfPlanePositions() != i + 1) || (newID != (currentID + 1)));
if (error)
{
MITK_TEST_CONDITION(m_Service->GetNumberOfPlanePositions() == i + 1,
"Checking for correct number of planepositions");
MITK_TEST_CONDITION(newID == (currentID + 1), "Testing for correct ID");
MITK_TEST_OUTPUT(<< "New: " << newID << " Last: " << currentID);
return EXIT_FAILURE;
}
currentID = newID;
}
unsigned int numberOfPlanePos = m_Service->GetNumberOfPlanePositions();
// Adding existing planes -> nothing should change
for (unsigned int i = 0; i < (m_Geometries.size() - 1) * 0.5; ++i)
{
unsigned int newID = m_Service->AddNewPlanePosition(m_Geometries.at(i * 2), i * 2);
error = ((m_Service->GetNumberOfPlanePositions() != numberOfPlanePos) || (newID != i * 2));
if (error)
{
MITK_TEST_CONDITION(m_Service->GetNumberOfPlanePositions() == numberOfPlanePos,
"Checking for correct number of planepositions");
MITK_TEST_CONDITION(newID == i * 2, "Testing for correct ID");
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
int testGetPlanePosition()
{
bool error(true);
MITK_TEST_OUTPUT(<< "Starting Test: ######### G e t P l a n e P o s i t i o n #########");
// Testing for existing planepositions
for (unsigned int i = 0; i < m_Geometries.size(); ++i)
{
auto plane = m_Geometries.at(i);
auto op = m_Service->GetPlanePosition(i);
error =
(!mitk::Equal(op->GetHeight(), plane->GetExtent(1)) || !mitk::Equal(op->GetWidth(), plane->GetExtent(0)) ||
!mitk::Equal(op->GetSpacing(), plane->GetSpacing()) ||
!mitk::Equal(op->GetTransform()->GetOffset(), plane->GetIndexToWorldTransform()->GetOffset()) ||
!mitk::Equal(op->GetDirectionVector().GetVnlVector(),
- plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).normalize()) ||
+ plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).normalize().as_ref()) ||
!mitk::MatrixEqualElementWise(op->GetTransform()->GetMatrix(), plane->GetIndexToWorldTransform()->GetMatrix()));
if (error)
{
MITK_TEST_OUTPUT(<< "Iteration: " << i)
MITK_TEST_CONDITION(
mitk::Equal(op->GetHeight(), plane->GetExtent(1)) && mitk::Equal(op->GetWidth(), plane->GetExtent(0)),
"Checking for correct extent");
MITK_TEST_CONDITION(mitk::Equal(op->GetSpacing(), plane->GetSpacing()), "Checking for correct spacing");
MITK_TEST_CONDITION(mitk::Equal(op->GetTransform()->GetOffset(), plane->GetIndexToWorldTransform()->GetOffset()),
"Checking for correct offset");
MITK_INFO << "Op: " << op->GetDirectionVector()
<< " plane: " << plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2) << "\n";
MITK_TEST_CONDITION(mitk::Equal(op->GetDirectionVector().GetVnlVector(),
- plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)),
+ plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref()),
"Checking for correct direction");
MITK_TEST_CONDITION(
mitk::MatrixEqualElementWise(op->GetTransform()->GetMatrix(), plane->GetIndexToWorldTransform()->GetMatrix()),
"Checking for correct matrix");
return EXIT_FAILURE;
}
}
// Testing for not existing planepositions
error = (m_Service->GetPlanePosition(100000000) != nullptr || m_Service->GetPlanePosition(-1) != nullptr);
if (error)
{
MITK_TEST_CONDITION(m_Service->GetPlanePosition(100000000) == nullptr, "Trying to get non existing pos");
MITK_TEST_CONDITION(m_Service->GetPlanePosition(-1) == nullptr, "Trying to get non existing pos");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
int testRemovePlanePosition()
{
MITK_TEST_OUTPUT(<< "Starting Test: ######### R e m o v e P l a n e P o s i t i o n #########");
unsigned int size = m_Service->GetNumberOfPlanePositions();
// Testing for invalid IDs
bool removed = m_Service->RemovePlanePosition(-1);
removed = m_Service->RemovePlanePosition(1000000);
unsigned int size2 = m_Service->GetNumberOfPlanePositions();
if (removed)
{
MITK_TEST_CONDITION(removed == false, "Testing remove not existing planepositions");
MITK_TEST_CONDITION(size == size2, "Testing remove not existing planepositions");
return EXIT_FAILURE;
}
// Testing for valid IDs
for (unsigned int i = 0; i < m_Geometries.size() * 0.5; i++)
{
removed = m_Service->RemovePlanePosition(i);
unsigned int size2 = m_Service->GetNumberOfPlanePositions();
removed = (size2 == (size - (i + 1)));
if (!removed)
{
MITK_TEST_CONDITION(removed == true, "Testing remove existing planepositions");
MITK_TEST_CONDITION(size == (size - i + 1), "Testing remove existing planepositions");
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
int testRemoveAll()
{
MITK_TEST_OUTPUT(<< "Starting Test: ######### R e m o v e A l l #########");
unsigned int numPos = m_Service->GetNumberOfPlanePositions();
MITK_INFO << numPos;
m_Service->RemoveAllPlanePositions();
bool error(true);
error = (m_Service->GetNumberOfPlanePositions() != 0 || m_Service->GetPlanePosition(60) != nullptr);
if (error)
{
MITK_TEST_CONDITION(m_Service->GetNumberOfPlanePositions() == 0, "Testing remove all pos");
MITK_TEST_CONDITION(m_Service->GetPlanePosition(60) == nullptr, "Testing remove all pos");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
int mitkPlanePositionManagerTest(int, char *[])
{
MITK_TEST_BEGIN("PlanePositionManager");
SetUpBeforeTest();
int result;
MITK_TEST_CONDITION_REQUIRED((result = testAddPlanePosition()) == EXIT_SUCCESS, "");
MITK_TEST_CONDITION_REQUIRED((result = testGetPlanePosition()) == EXIT_SUCCESS, "");
MITK_TEST_CONDITION_REQUIRED((result = testRemovePlanePosition()) == EXIT_SUCCESS, "");
MITK_TEST_CONDITION_REQUIRED((result = testRemoveAll()) == EXIT_SUCCESS, "");
MITK_TEST_END();
}
diff --git a/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp b/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp
index 2139114345..6229d1b1bf 100644
--- a/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp
+++ b/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp
@@ -1,253 +1,253 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <mitkIOUtil.h>
#include <mitkImagePixelReadAccessor.h>
#include <mitkImageWriteAccessor.h>
#include <mitkTestingMacros.h>
#include "mitkSurfaceToImageFilter.h"
#include "mitkTestFixture.h"
#include <vtkPolyData.h>
class mitkSurfaceToImageFilterTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkSurfaceToImageFilterTestSuite);
MITK_TEST(test3DSurfaceValidOutput);
MITK_TEST(test3DSurfaceCorrect);
MITK_TEST(test3DSurfaceIn4DImage);
CPPUNIT_TEST_SUITE_END();
private:
/** Members used inside the different test methods. All members are initialized via setUp().*/
mitk::Surface::Pointer m_Surface;
public:
/**
* @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used
* members for a new test case. (If the members are not used in a test, the method does not need to be called).
*/
void setUp() override { m_Surface = mitk::IOUtil::Load<mitk::Surface>(GetTestDataFilePath("ball.stl")); }
void tearDown() override {}
void test3DSurfaceValidOutput()
{
mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New();
mitk::Image::Pointer additionalInputImage = mitk::Image::New();
additionalInputImage->Initialize(mitk::MakeScalarPixelType<unsigned int>(), *m_Surface->GetTimeGeometry());
// Arrange the filter
surfaceToImageFilter->MakeOutputBinaryOn();
surfaceToImageFilter->SetInput(m_Surface);
surfaceToImageFilter->SetImage(additionalInputImage);
surfaceToImageFilter->Update();
CPPUNIT_ASSERT_MESSAGE(
"SurfaceToImageFilter_AnyInputImageAndModeSetToBinary_ResultIsImageWithUCHARPixelType",
- surfaceToImageFilter->GetOutput()->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR);
+ surfaceToImageFilter->GetOutput()->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR);
surfaceToImageFilter->SetUShortBinaryPixelType(true);
surfaceToImageFilter->Update();
CPPUNIT_ASSERT_MESSAGE(
"SurfaceToImageFilter_AnyInputImageAndModeSetToBinary_ResultIsImageWithUCHARPixelType",
- surfaceToImageFilter->GetOutput()->GetPixelType().GetComponentType() == itk::ImageIOBase::USHORT);
+ surfaceToImageFilter->GetOutput()->GetPixelType().GetComponentType() == itk::IOComponentEnum::USHORT);
}
void test3DSurfaceCorrect()
{
mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New();
// todo I don't know if this image is always needed. There is no documentation of the filter. Use git blame and ask
// the author.
mitk::Image::Pointer additionalInputImage = mitk::Image::New();
auto *dims = new unsigned int[3];
dims[0] = 32;
dims[1] = 32;
dims[2] = 32;
additionalInputImage->Initialize(mitk::MakeScalarPixelType<unsigned int>(), 3, dims);
additionalInputImage->SetOrigin(m_Surface->GetGeometry()->GetOrigin());
additionalInputImage->GetGeometry()->SetIndexToWorldTransform(m_Surface->GetGeometry()->GetIndexToWorldTransform());
// Arrange the filter
// The docu does not really tell if this is always needed. Could we skip SetImage in any case?
surfaceToImageFilter->MakeOutputBinaryOn();
surfaceToImageFilter->SetInput(m_Surface);
surfaceToImageFilter->SetImage(additionalInputImage);
surfaceToImageFilter->Update();
mitk::ImagePixelReadAccessor<unsigned char, 3> outputReader(surfaceToImageFilter->GetOutput());
itk::Index<3> idx;
bool valuesCorrect = true;
// Values outside the ball should be 0
idx[0] = 0;
idx[1] = 0, idx[2] = 0;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0);
idx[0] = 0;
idx[1] = 15, idx[2] = 15;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0);
idx[0] = 15;
idx[1] = 15, idx[2] = 0;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0);
idx[0] = 15;
idx[1] = 0, idx[2] = 15;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0);
idx[0] = 5;
idx[1] = 9, idx[2] = 23;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0);
// Values inside the ball should be 1
idx[0] = 15;
idx[1] = 15, idx[2] = 15;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1);
idx[0] = 31;
idx[1] = 15, idx[2] = 15;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1);
idx[0] = 2;
idx[1] = 15, idx[2] = 15;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1);
idx[0] = 15;
idx[1] = 15, idx[2] = 2;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1);
idx[0] = 15;
idx[1] = 2, idx[2] = 15;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1);
idx[0] = 6;
idx[1] = 9, idx[2] = 23;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1);
CPPUNIT_ASSERT_MESSAGE("SurfaceToImageFilter_BallSurfaceAsInput_OutputCorrect", valuesCorrect == true);
}
void test3DSurfaceIn4DImage()
{
mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New();
mitk::Image::Pointer additionalInputImage = mitk::Image::New();
auto *dims = new unsigned int[4];
dims[0] = 32;
dims[1] = 32;
dims[2] = 32;
dims[3] = 2;
additionalInputImage->Initialize(mitk::MakeScalarPixelType<unsigned int>(), 4, dims);
additionalInputImage->SetOrigin(m_Surface->GetGeometry()->GetOrigin());
additionalInputImage->GetGeometry()->SetIndexToWorldTransform(m_Surface->GetGeometry()->GetIndexToWorldTransform());
mitk::Image::Pointer secondStep = additionalInputImage->Clone();
unsigned int size = sizeof(unsigned char);
for (unsigned int i = 0; i < secondStep->GetDimension(); ++i)
{
size *= secondStep->GetDimension(i);
}
{
mitk::ImageWriteAccessor accessor(secondStep);
memset(accessor.GetData(), 1, size);
}
additionalInputImage->GetTimeGeometry()->Expand(2);
additionalInputImage->GetGeometry(1)->SetSpacing(secondStep->GetGeometry()->GetSpacing());
additionalInputImage->GetGeometry(1)->SetOrigin(secondStep->GetGeometry()->GetOrigin());
additionalInputImage->GetGeometry(1)->SetIndexToWorldTransform(
secondStep->GetGeometry()->GetIndexToWorldTransform());
{
mitk::ImageReadAccessor readAccess(secondStep);
additionalInputImage->SetImportVolume(readAccess.GetData(), 0);
additionalInputImage->SetImportVolume(readAccess.GetData(), 1);
}
// Arrange the filter
surfaceToImageFilter->MakeOutputBinaryOn();
surfaceToImageFilter->SetInput(m_Surface);
surfaceToImageFilter->SetImage(additionalInputImage);
surfaceToImageFilter->Update();
mitk::ImagePixelReadAccessor<unsigned char, 4> outputReader(surfaceToImageFilter->GetOutput());
itk::Index<4> idx;
bool valuesCorrect = true;
// Values outside the ball should be 0
idx[0] = 0;
idx[1] = 0, idx[2] = 0;
idx[3] = 0;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0);
idx[0] = 0;
idx[1] = 15, idx[2] = 15;
idx[3] = 0;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0);
idx[0] = 15;
idx[1] = 15, idx[2] = 0;
idx[3] = 0;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0);
idx[0] = 15;
idx[1] = 0, idx[2] = 15;
idx[3] = 0;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0);
idx[0] = 5;
idx[1] = 9, idx[2] = 23;
idx[3] = 0;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0);
// Values inside the ball should be 1 hould be 1
idx[0] = 15;
idx[1] = 15, idx[2] = 15;
idx[3] = 0;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1);
idx[0] = 31;
idx[1] = 15, idx[2] = 15;
idx[3] = 0;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1);
idx[0] = 2;
idx[1] = 15, idx[2] = 15;
idx[3] = 0;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1);
idx[0] = 15;
idx[1] = 15, idx[2] = 2;
idx[3] = 0;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1);
idx[0] = 15;
idx[1] = 2, idx[2] = 15;
idx[3] = 0;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1);
idx[0] = 6;
idx[1] = 9, idx[2] = 23;
idx[3] = 0;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1);
// Values inside the ball but in the second timestep hould be 0
idx[0] = 15;
idx[1] = 15, idx[2] = 15;
idx[3] = 1;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0);
idx[0] = 31;
idx[1] = 15, idx[2] = 15;
idx[3] = 1;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0);
idx[0] = 2;
idx[1] = 15, idx[2] = 15;
idx[3] = 1;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0);
idx[0] = 15;
idx[1] = 15, idx[2] = 2;
idx[3] = 1;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0);
idx[0] = 15;
idx[1] = 2, idx[2] = 15;
idx[3] = 1;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0);
idx[0] = 6;
idx[1] = 9, idx[2] = 23;
idx[3] = 1;
valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0);
CPPUNIT_ASSERT_MESSAGE("SurfaceToImageFilter_BallSurfaceAsInput_Output4DCorrect", valuesCorrect == true);
}
};
MITK_TEST_SUITE_REGISTRATION(mitkSurfaceToImageFilter)
diff --git a/Modules/Core/test/mitkTinyXMLTest.cpp b/Modules/Core/test/mitkTinyXMLTest.cpp
index 392ab4384d..db0d7104d2 100644
--- a/Modules/Core/test/mitkTinyXMLTest.cpp
+++ b/Modules/Core/test/mitkTinyXMLTest.cpp
@@ -1,162 +1,163 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// Testing
#include "mitkTestFixture.h"
#include <mitkTestingMacros.h>
// std includes
#include <cmath>
#include <iomanip>
#include <tinyxml2.h>
// MITK includes
#include "mitkStringProperty.h"
#include <mitkNumericTypes.h>
+#include <mitkUtf8Util.h>
// itksys
#include <itksys/SystemTools.hxx>
// VTK includes
#include <vtkDebugLeaks.h>
// vnl includes
#include <vnl/vnl_vector_fixed.hxx>
class mitkTinyXMLTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkTinyXMLTestSuite);
MITK_TEST(TestingFunctionSetupWorks_Success);
MITK_TEST(TestingReadValueFromSetupDocument_Success);
MITK_TEST(TestingReadOutValueWorks_Success);
MITK_TEST(TestDoubleValueWriteOut_Success);
MITK_TEST(TestDoubleValueWriteOutManyDecimalPlaces_Success);
CPPUNIT_TEST_SUITE_END();
private:
- const std::string m_Filename = itksys::SystemTools::GetCurrentWorkingDirectory() + "/TinyXMLTest.txt";
+ const std::string m_Filename = mitk::Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetCurrentWorkingDirectory()) + "/TinyXMLTest.txt";
const std::string m_ElementToStoreAttributeName = "DoubleTest";
const std::string m_AttributeToStoreName = "CommaValue";
tinyxml2::XMLDocument m_Document;
tinyxml2::XMLElement *m_DoubleTest;
double calcPrecision(const unsigned int requiredDecimalPlaces)
{
return pow(10.0, -1.0 * ((double)requiredDecimalPlaces));
}
bool Setup(double valueToWrite)
{
m_Document.Clear();
m_DoubleTest = nullptr;
// 1. create simple document
m_Document.InsertEndChild(m_Document.NewDeclaration());
auto *version = m_Document.NewElement("Version");
version->SetAttribute("Writer", __FILE__);
version->SetAttribute("CVSRevision", "$Revision: 17055 $");
version->SetAttribute("FileVersion", 1);
m_Document.InsertEndChild(version);
// 2. store one element containing a double value with potentially many after comma digits.
auto *vElement = m_Document.NewElement(m_ElementToStoreAttributeName.c_str());
vElement->SetAttribute(m_AttributeToStoreName.c_str(), valueToWrite);
m_Document.InsertEndChild(vElement);
// 3. store in file.
auto err = m_Document.SaveFile(m_Filename.c_str());
return tinyxml2::XML_SUCCESS == err;
}
public:
void setUp() override {}
void tearDown() override {}
void TestingFunctionSetupWorks_Success()
{
CPPUNIT_ASSERT_MESSAGE("Test if Setup correctly writes data to file", Setup(1.0));
}
int readValueFromSetupDocument(double &readOutValue)
{
if (tinyxml2::XML_SUCCESS != m_Document.LoadFile(m_Filename.c_str()))
{
CPPUNIT_ASSERT_MESSAGE("Test Setup failed, could not open file", false);
return tinyxml2::XML_NO_ATTRIBUTE;
}
else
{
m_DoubleTest = m_Document.FirstChildElement(m_ElementToStoreAttributeName.c_str());
return m_DoubleTest->QueryDoubleAttribute(m_AttributeToStoreName.c_str(), &readOutValue);
}
}
void TestingReadValueFromSetupDocument_Success()
{
if (tinyxml2::XML_SUCCESS != m_Document.LoadFile(m_Filename.c_str()))
{
CPPUNIT_ASSERT_MESSAGE("Test Setup failed, could not open file", tinyxml2::XML_SUCCESS != m_Document.LoadFile(m_Filename.c_str()));
}
else
{
m_DoubleTest = m_Document.FirstChildElement(m_ElementToStoreAttributeName.c_str());
CPPUNIT_ASSERT_MESSAGE("Test Setup could open file", m_DoubleTest != nullptr);
}
}
/**
* this first test ensures we can correctly readout values from the
* TinyXMLDocument.
*/
void TestingReadOutValueWorks_Success()
{
double readValue;
CPPUNIT_ASSERT_MESSAGE("checking if readout mechanism works.",
tinyxml2::XML_SUCCESS == readValueFromSetupDocument(readValue));
}
void TestDoubleValueWriteOut_Success()
{
const double valueToWrite = -1.123456;
const int validDigitsAfterComma = 6; // indicates the number of valid digits after comma of valueToWrite
const double neededPrecision = calcPrecision(validDigitsAfterComma + 1);
double readValue;
Setup(valueToWrite);
readValueFromSetupDocument(readValue);
CPPUNIT_ASSERT_MESSAGE("Testing if value valueToWrite equals readValue which was retrieved from TinyXML document",
mitk::Equal(valueToWrite, readValue, neededPrecision));
}
void TestDoubleValueWriteOutManyDecimalPlaces_Success()
{
const double valueToWrite = -1.12345678910111;
const int validDigitsAfterComma = 14; // indicates the number of valid digits after comma of valueToWrite
const double neededPrecision = calcPrecision(validDigitsAfterComma + 1);
double readValue;
Setup(valueToWrite);
readValueFromSetupDocument(readValue);
CPPUNIT_ASSERT_MESSAGE("Testing if value valueToWrite equals readValue which was retrieved from TinyXML document",
mitk::Equal(valueToWrite, readValue, neededPrecision));
}
};
MITK_TEST_SUITE_REGISTRATION(mitkTinyXML)
diff --git a/Modules/CppMicroServices/cmake/usGlobalConfig.h.in b/Modules/CppMicroServices/cmake/usGlobalConfig.h.in
index 441e3eae5b..c1b6b630cc 100644
--- a/Modules/CppMicroServices/cmake/usGlobalConfig.h.in
+++ b/Modules/CppMicroServices/cmake/usGlobalConfig.h.in
@@ -1,206 +1,206 @@
/*
USCONFIG.h
this file is generated. Do not change!
*/
#ifndef USGLOBALCONFIG_H
#define USGLOBALCONFIG_H
#cmakedefine US_BUILD_SHARED_LIBS
#cmakedefine US_ENABLE_THREADING_SUPPORT
#cmakedefine US_GCC_RTTI_WORKAROUND_NEEDED
#cmakedefine US_HAVE_VISIBILITY_ATTRIBUTE
//-------------------------------------------------------------------
// Header Availability
//-------------------------------------------------------------------
#cmakedefine US_HAVE_CXXABI_H
#cmakedefine US_HAVE_STDINT_H
#cmakedefine US_HAVE_TR1_UNORDERED_MAP_H
#cmakedefine US_HAVE_TR1_UNORDERED_SET_H
#cmakedefine US_HAVE_TR1_FUNCTIONAL_H
#cmakedefine US_HAVE_UNORDERED_MAP_H
#cmakedefine US_HAVE_UNORDERED_SET_H
#cmakedefine US_HAVE_FUNCTIONAL_H
#cmakedefine US_HAVE_TR1_UNORDERED_MAP
#cmakedefine US_HAVE_TR1_UNORDERED_SET
#cmakedefine US_HAVE_TR1_FUNCTION
#cmakedefine US_HAVE_STD_UNORDERED_MAP
#cmakedefine US_HAVE_STD_UNORDERED_SET
#cmakedefine US_HAVE_STD_FUNCTION
#cmakedefine US_HAVE_TR1_HASH
#cmakedefine US_HAVE_TR1_HASH_STRUCT
#cmakedefine US_HAVE_TR1_HASH_CLASS
#cmakedefine US_HAVE_STD_HASH
#cmakedefine US_HAVE_STD_HASH_STRUCT
#cmakedefine US_HAVE_STD_HASH_CLASS
///-------------------------------------------------------------------
// Version information
//-------------------------------------------------------------------
#define CppMicroServices_MAJOR_VERSION @CppMicroServices_MAJOR_VERSION@
#define CppMicroServices_MINOR_VERSION @CppMicroServices_MINOR_VERSION@
#define CppMicroServices_PATCH_VERSION @CppMicroServices_PATCH_VERSION@
#define CppMicroServices_VERSION @CppMicroServices_VERSION@
#define CppMicroServices_VERSION_STR "@CppMicroServices_VERSION@"
#define US_MAJOR_VERSION @CppMicroServices_MAJOR_VERSION@
#define US_MINOR_VERSION @CppMicroServices_MINOR_VERSION@
#define US_PATCH_VERSION @CppMicroServices_PATCH_VERSION@
#define US_VERSION @CppMicroServices_VERSION@
#define US_VERSION_STR "@CppMicroServices_VERSION@"
//-------------------------------------------------------------------
// Namespace customization
//-------------------------------------------------------------------
#define US_NAMESPACE @US_NAMESPACE@
#ifndef US_NAMESPACE /* user namespace */
# define US_PREPEND_NAMESPACE(name) ::name
# define US_USE_NAMESPACE
# define US_BEGIN_NAMESPACE
# define US_END_NAMESPACE
# define US_FORWARD_DECLARE_CLASS(name) class name;
# define US_FORWARD_DECLARE_STRUCT(name) struct name;
#else /* user namespace */
# define US_PREPEND_NAMESPACE(name) ::US_NAMESPACE::name
# define US_USE_NAMESPACE using namespace ::US_NAMESPACE;
# define US_BEGIN_NAMESPACE namespace US_NAMESPACE {
# define US_END_NAMESPACE }
# define US_FORWARD_DECLARE_CLASS(name) \
US_BEGIN_NAMESPACE class name; US_END_NAMESPACE
# define US_FORWARD_DECLARE_STRUCT(name) \
US_BEGIN_NAMESPACE struct name; US_END_NAMESPACE
namespace US_NAMESPACE {}
#endif /* user namespace */
//-------------------------------------------------------------------
// Platform defines
//-------------------------------------------------------------------
#if defined(__APPLE__)
#define US_PLATFORM_APPLE
#endif
#if defined(__linux__)
#define US_PLATFORM_LINUX
#endif
#if defined(_WIN32) || defined(_WIN64)
#define US_PLATFORM_WINDOWS
#else
#define US_PLATFORM_POSIX
#endif
///-------------------------------------------------------------------
// Macros for import/export declarations
//-------------------------------------------------------------------
#if defined(US_PLATFORM_WINDOWS)
#define US_ABI_EXPORT __declspec(dllexport)
#define US_ABI_IMPORT __declspec(dllimport)
#define US_ABI_LOCAL
#elif defined(US_HAVE_VISIBILITY_ATTRIBUTE)
#define US_ABI_EXPORT __attribute__ ((visibility ("default")))
#define US_ABI_IMPORT __attribute__ ((visibility ("default")))
#define US_ABI_LOCAL __attribute__ ((visibility ("hidden")))
#else
#define US_ABI_EXPORT
#define US_ABI_IMPORT
#define US_ABI_LOCAL
#endif
//-------------------------------------------------------------------
// Macros for suppressing warnings
//-------------------------------------------------------------------
#ifdef _MSC_VER
#define US_MSVC_PUSH_DISABLE_WARNING(wn) \
__pragma(warning(push)) \
__pragma(warning(disable:wn))
#define US_MSVC_POP_WARNING \
__pragma(warning(pop))
#define US_MSVC_DISABLE_WARNING(wn) \
__pragma(warning(disable:wn))
#else
#define US_MSVC_PUSH_DISABLE_WARNING(wn)
#define US_MSVC_POP_WARNING
#define US_MSVC_DISABLE_WARNING(wn)
#endif
// Do not warn about the usage of deprecated unsafe functions
US_MSVC_DISABLE_WARNING(4996)
// Mark a variable or expression result as unused
#define US_UNUSED(x) (void)(x)
//-------------------------------------------------------------------
// Hash Container
//-------------------------------------------------------------------
#ifdef US_HAVE_UNORDERED_MAP_H
#include <unordered_map>
#elif defined(US_HAVE_TR1_UNORDERED_MAP_H)
#include <tr1/unordered_map>
#endif
#ifdef US_HAVE_UNORDERED_SET_H
#include <unordered_set>
#elif defined(US_HAVE_TR1_UNORDERED_SET_H)
#include <tr1/unordered_set>
#endif
#ifdef US_HAVE_STD_UNORDERED_MAP
#define US_UNORDERED_MAP_TYPE ::std::unordered_map
#elif defined(US_HAVE_TR1_UNORDERED_MAP)
#define US_UNORDERED_MAP_TYPE ::std::tr1::unordered_map
#endif
#ifdef US_HAVE_STD_UNORDERED_SET
#define US_UNORDERED_SET_TYPE ::std::unordered_set
#elif defined(US_HAVE_TR1_UNORDERED_SET)
#define US_UNORDERED_SET_TYPE ::std::tr1::unordered_set
#endif
#ifdef US_HAVE_STD_HASH
#define US_HASH_FUNCTION_NAMESPACE ::std
#ifdef US_HAVE_STD_HASH_STRUCT
#define US_HASH_FUNCTION_FRIEND(type) friend struct ::std::hash<type>
#elif defined(US_HAVE_STD_HASH_CLASS)
#define US_HASH_FUNCTION_FRIEND(type) friend class ::std::hash<type>
#endif
#define US_HASH_FUNCTION_NAMESPACE_BEGIN namespace std {
#define US_HASH_FUNCTION_NAMESPACE_END }
#elif defined(US_HAVE_TR1_HASH)
#define US_HASH_FUNCTION_NAMESPACE ::std::tr1
#ifdef US_HAVE_TR1_HASH_STRUCT
#define US_HASH_FUNCTION_FRIEND(type) friend struct ::std::tr1::hash<type>
#elif defined(US_HAVE_TR1_HASH_CLASS)
#define US_HASH_FUNCTION_FRIEND(type) friend class ::std::tr1::hash<type>
#endif
#define US_HASH_FUNCTION_NAMESPACE_BEGIN namespace std { namespace tr1 {
#define US_HASH_FUNCTION_NAMESPACE_END }}
#endif
#define US_HASH_FUNCTION_BEGIN(type) \
template<> \
-struct hash<type> : std::unary_function<type, std::size_t> { \
+struct hash<type> { \
std::size_t operator()(const type& arg) const {
#define US_HASH_FUNCTION_END } };
#define US_HASH_FUNCTION(type, arg) hash<type>()(arg)
#endif // USGLOBALCONFIG_H
diff --git a/Modules/CppMicroServices/core/src/module/usModuleManifest.cpp b/Modules/CppMicroServices/core/src/module/usModuleManifest.cpp
index 8ae2699cab..904d538cb7 100644
--- a/Modules/CppMicroServices/core/src/module/usModuleManifest.cpp
+++ b/Modules/CppMicroServices/core/src/module/usModuleManifest.cpp
@@ -1,154 +1,154 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#include "usModuleManifest_p.h"
#include "jsoncpp.h"
#include <stdexcept>
US_BEGIN_NAMESPACE
namespace {
typedef std::map<std::string, Any> AnyMap;
typedef std::vector<Any> AnyVector;
void ParseJsonObject(const Json::Value& jsonObject, AnyMap& anyMap);
void ParseJsonArray(const Json::Value& jsonArray, AnyVector& anyVector);
Any ParseJsonValue(const Json::Value& jsonValue)
{
if (jsonValue.isObject())
{
Any any = AnyMap();
ParseJsonObject(jsonValue, ref_any_cast<AnyMap>(any));
return any;
}
else if (jsonValue.isArray())
{
Any any = AnyVector();
ParseJsonArray(jsonValue, ref_any_cast<AnyVector>(any));
return any;
}
else if (jsonValue.isString())
{
return Any(jsonValue.asString());
}
else if (jsonValue.isBool())
{
return Any(jsonValue.asBool());
}
- else if (jsonValue.isDouble())
- {
- return Any(jsonValue.asDouble());
- }
else if (jsonValue.isIntegral())
{
return Any(jsonValue.asInt());
}
+ else if (jsonValue.isDouble())
+ {
+ return Any(jsonValue.asDouble());
+ }
return Any();
}
void ParseJsonObject(const Json::Value& jsonObject, AnyMap& anyMap)
{
for (Json::Value::const_iterator it = jsonObject.begin();
it != jsonObject.end(); ++it)
{
const Json::Value& jsonValue = *it;
Any anyValue = ParseJsonValue(jsonValue);
if (!anyValue.Empty())
{
- anyMap.insert(std::make_pair(it.memberName(), anyValue));
+ anyMap.insert(std::make_pair(it.name(), anyValue));
}
}
}
void ParseJsonArray(const Json::Value& jsonArray, AnyVector& anyVector)
{
for (Json::Value::const_iterator it = jsonArray.begin();
it != jsonArray.end(); ++it)
{
const Json::Value& jsonValue = *it;
Any anyValue = ParseJsonValue(jsonValue);
if (!anyValue.Empty())
{
anyVector.push_back(anyValue);
}
}
}
}
ModuleManifest::ModuleManifest()
{
}
void ModuleManifest::Parse(std::istream& is)
{
Json::Value root;
Json::Reader jsonReader(Json::Features::strictMode());
if (!jsonReader.parse(is, root, false))
{
throw std::runtime_error(jsonReader.getFormattedErrorMessages());
}
if (!root.isObject())
{
throw std::runtime_error("The Json root element must be an object.");
}
ParseJsonObject(root, m_Properties);
}
bool ModuleManifest::Contains(const std::string& key) const
{
return m_Properties.count(key) > 0;
}
Any ModuleManifest::GetValue(const std::string& key) const
{
AnyMap::const_iterator iter = m_Properties.find(key);
if (iter != m_Properties.end())
{
return iter->second;
}
return Any();
}
std::vector<std::string> ModuleManifest::GetKeys() const
{
std::vector<std::string> keys;
for (AnyMap::const_iterator iter = m_Properties.begin();
iter != m_Properties.end(); ++iter)
{
keys.push_back(iter->first);
}
return keys;
}
void ModuleManifest::SetValue(const std::string& key, const Any& value)
{
m_Properties[key] = value;
}
US_END_NAMESPACE
diff --git a/Modules/CppMicroServices/core/src/util/usListenerFunctors_p.h b/Modules/CppMicroServices/core/src/util/usListenerFunctors_p.h
index f8364127ac..6578eb7ed4 100644
--- a/Modules/CppMicroServices/core/src/util/usListenerFunctors_p.h
+++ b/Modules/CppMicroServices/core/src/util/usListenerFunctors_p.h
@@ -1,89 +1,88 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#ifndef USLISTENERFUNCTORS_P_H
#define USLISTENERFUNCTORS_P_H
#include <usServiceEvent.h>
#include <usModuleEvent.h>
#include <algorithm>
#include <cstring>
#ifdef US_HAVE_STD_FUNCTION
#ifdef US_HAVE_FUNCTIONAL_H
#include <functional>
#elif defined(US_HAVE_TR1_FUNCTIONAL_H)
#include <tr1/functional>
#endif
#define US_FUNCTION_TYPE std::function
#elif defined(US_HAVE_TR1_FUNCTION)
#ifdef US_HAVE_TR1_FUNCTIONAL_H
#include <tr1/functional>
#elif defined(US_HAVE_FUNCTIONAL_H)
#include <functional>
#endif
#define US_FUNCTION_TYPE std::tr1::function
#endif
#define US_MODULE_LISTENER_FUNCTOR US_FUNCTION_TYPE<void(const US_PREPEND_NAMESPACE(ModuleEvent)&)>
#define US_SERVICE_LISTENER_FUNCTOR US_FUNCTION_TYPE<void(const US_PREPEND_NAMESPACE(ServiceEvent)&)>
US_BEGIN_NAMESPACE
template<class X>
US_MODULE_LISTENER_FUNCTOR ModuleListenerMemberFunctor(X* x, void (X::*memFn)(const US_PREPEND_NAMESPACE(ModuleEvent)))
{ return std::bind(std::mem_fn(memFn), x, std::placeholders::_1); }
- struct ModuleListenerCompare : std::binary_function<std::pair<US_MODULE_LISTENER_FUNCTOR, void*>,
- std::pair<US_MODULE_LISTENER_FUNCTOR, void*>, bool>
+ struct ModuleListenerCompare
{
bool operator()(const std::pair<US_MODULE_LISTENER_FUNCTOR, void*>& p1,
const std::pair<US_MODULE_LISTENER_FUNCTOR, void*>& p2) const
{
return p1.second == p2.second &&
p1.first.target<void(const US_PREPEND_NAMESPACE(ModuleEvent)&)>() == p2.first.target<void(const US_PREPEND_NAMESPACE(ModuleEvent)&)>();
}
};
template<class X>
US_SERVICE_LISTENER_FUNCTOR ServiceListenerMemberFunctor(X* x, void (X::*memFn)(const US_PREPEND_NAMESPACE(ServiceEvent)))
{ return std::bind(std::mem_fn(memFn), x, std::placeholders::_1); }
- struct ServiceListenerCompare : std::binary_function<US_SERVICE_LISTENER_FUNCTOR, US_SERVICE_LISTENER_FUNCTOR, bool>
+ struct ServiceListenerCompare
{
bool operator()(const US_SERVICE_LISTENER_FUNCTOR& f1,
const US_SERVICE_LISTENER_FUNCTOR& f2) const
{
return f1.target<void(const US_PREPEND_NAMESPACE(ServiceEvent)&)>() == f2.target<void(const US_PREPEND_NAMESPACE(ServiceEvent)&)>();
}
};
US_END_NAMESPACE
US_HASH_FUNCTION_NAMESPACE_BEGIN
US_HASH_FUNCTION_BEGIN(US_SERVICE_LISTENER_FUNCTOR)
void(*targetFunc)(const US_PREPEND_NAMESPACE(ServiceEvent)&) = arg.target<void(const US_PREPEND_NAMESPACE(ServiceEvent)&)>();
void* targetPtr = nullptr;
std::memcpy(&targetPtr, &targetFunc, sizeof(void*));
return US_HASH_FUNCTION(void*, targetPtr);
US_HASH_FUNCTION_END
US_HASH_FUNCTION_NAMESPACE_END
#endif // USLISTENERFUNCTORS_P_H
diff --git a/Modules/CppMicroServices/third_party/jsoncpp.cpp b/Modules/CppMicroServices/third_party/jsoncpp.cpp
index e4d9ea08a6..52ccdf35fa 100644
--- a/Modules/CppMicroServices/third_party/jsoncpp.cpp
+++ b/Modules/CppMicroServices/third_party/jsoncpp.cpp
@@ -1,4225 +1,5337 @@
-/// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/).
-/// It is intented to be used with #include <json/json.h>
+/// Json-cpp amalgamated source (http://jsoncpp.sourceforge.net/).
+/// It is intended to be used with #include "json/json.h"
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: LICENSE
// //////////////////////////////////////////////////////////////////////
/*
The JsonCpp library's source code, including accompanying documentation,
tests and demonstration applications, are licensed under the following
conditions...
-The author (Baptiste Lepilleur) explicitly disclaims copyright in all
+Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
jurisdictions which recognize such a disclaimer. In such jurisdictions,
this software is released into the Public Domain.
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
-2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is
-released under the terms of the MIT License (see below).
+2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
+The JsonCpp Authors, and is released under the terms of the MIT License (see below).
In jurisdictions which recognize Public Domain property, the user of this
software may choose to accept it either as 1) Public Domain, 2) under the
conditions of the MIT License (see below), or 3) under the terms of dual
Public Domain/MIT License conditions described here, as they choose.
The MIT License is about as close to Public Domain as a license can get, and is
described in clear, concise terms at:
http://en.wikipedia.org/wiki/MIT_License
The full text of the MIT License follows:
========================================================================
-Copyright (c) 2007-2010 Baptiste Lepilleur
+Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
(END LICENSE TEXT)
The MIT license is compatible with both the GPL and commercial
software, affording one all of the rights of Public Domain with the
minor nuisance of being required to keep the above copyright notice
and license text in the source code. Note also that by accepting the
Public Domain "license" you can re-license your copy using whatever
license you like.
*/
// //////////////////////////////////////////////////////////////////////
// End of content of file: LICENSE
// //////////////////////////////////////////////////////////////////////
-#include <jsoncpp.h>
+#include "jsoncpp.h"
+
+#ifndef JSON_IS_AMALGAMATION
+#error "Compile with -I PATH_TO_JSON_DIRECTORY"
+#endif
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: src/lib_json/json_tool.h
// //////////////////////////////////////////////////////////////////////
-// Copyright 2007-2010 Baptiste Lepilleur
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED
-# define LIB_JSONCPP_JSON_TOOL_H_INCLUDED
+#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include <json/config.h>
+#endif
+
+// Also support old flag NO_LOCALE_SUPPORT
+#ifdef NO_LOCALE_SUPPORT
+#define JSONCPP_NO_LOCALE_SUPPORT
+#endif
+
+#ifndef JSONCPP_NO_LOCALE_SUPPORT
+#include <clocale>
+#endif
/* This header provides common string manipulation support, such as UTF-8,
* portable conversion from/to string...
*
* It is an internal header that must not be exposed.
*/
namespace Json {
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunneeded-internal-declaration"
+#endif
+static inline char getDecimalPoint() {
+#ifdef JSONCPP_NO_LOCALE_SUPPORT
+ return '\0';
+#else
+ struct lconv* lc = localeconv();
+ return lc ? *(lc->decimal_point) : '\0';
+#endif
+}
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
/// Converts a unicode code-point to UTF-8.
-static inline std::string
-codePointToUTF8(unsigned int cp)
-{
- std::string result;
-
- // based on description from http://en.wikipedia.org/wiki/UTF-8
-
- if (cp <= 0x7f)
- {
- result.resize(1);
- result[0] = static_cast<char>(cp);
- }
- else if (cp <= 0x7FF)
- {
- result.resize(2);
- result[1] = static_cast<char>(0x80 | (0x3f & cp));
- result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
- }
- else if (cp <= 0xFFFF)
- {
- result.resize(3);
- result[2] = static_cast<char>(0x80 | (0x3f & cp));
- result[1] = 0x80 | static_cast<char>((0x3f & (cp >> 6)));
- result[0] = 0xE0 | static_cast<char>((0xf & (cp >> 12)));
- }
- else if (cp <= 0x10FFFF)
- {
- result.resize(4);
- result[3] = static_cast<char>(0x80 | (0x3f & cp));
- result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
- result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
- result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
- }
-
- return result;
-}
-
-
-/// Returns true if ch is a control character (in range [0,32[).
-static inline bool
-isControlCharacter(char ch)
-{
- return ch > 0 && ch <= 0x1F;
+static inline String codePointToUTF8(unsigned int cp) {
+ String result;
+
+ // based on description from http://en.wikipedia.org/wiki/UTF-8
+
+ if (cp <= 0x7f) {
+ result.resize(1);
+ result[0] = static_cast<char>(cp);
+ } else if (cp <= 0x7FF) {
+ result.resize(2);
+ result[1] = static_cast<char>(0x80 | (0x3f & cp));
+ result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
+ } else if (cp <= 0xFFFF) {
+ result.resize(3);
+ result[2] = static_cast<char>(0x80 | (0x3f & cp));
+ result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
+ result[0] = static_cast<char>(0xE0 | (0xf & (cp >> 12)));
+ } else if (cp <= 0x10FFFF) {
+ result.resize(4);
+ result[3] = static_cast<char>(0x80 | (0x3f & cp));
+ result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
+ result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
+ result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
+ }
+
+ return result;
}
-
enum {
- /// Constant that specify the size of the buffer that must be passed to uintToString.
- uintToStringBufferSize = 3*sizeof(LargestUInt)+1
+ /// Constant that specify the size of the buffer that must be passed to
+ /// uintToString.
+ uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1
};
// Defines a char buffer for use with uintToString().
-typedef char UIntToStringBuffer[uintToStringBufferSize];
-
+using UIntToStringBuffer = char[uintToStringBufferSize];
/** Converts an unsigned integer to string.
- * @param value Unsigned interger to convert to string
+ * @param value Unsigned integer to convert to string
* @param current Input/Output string buffer.
* Must have at least uintToStringBufferSize chars free.
*/
-static inline void
-uintToString( LargestUInt value,
- char *&current )
-{
- *--current = 0;
- do
- {
- *--current = char(value % 10) + '0';
- value /= 10;
- }
- while ( value != 0 );
+static inline void uintToString(LargestUInt value, char*& current) {
+ *--current = 0;
+ do {
+ *--current = static_cast<char>(value % 10U + static_cast<unsigned>('0'));
+ value /= 10;
+ } while (value != 0);
}
-} // namespace Json {
+/** Change ',' to '.' everywhere in buffer.
+ *
+ * We had a sophisticated way, but it did not work in WinCE.
+ * @see https://github.com/open-source-parsers/jsoncpp/pull/9
+ */
+template <typename Iter> Iter fixNumericLocale(Iter begin, Iter end) {
+ for (; begin != end; ++begin) {
+ if (*begin == ',') {
+ *begin = '.';
+ }
+ }
+ return begin;
+}
+
+template <typename Iter> void fixNumericLocaleInput(Iter begin, Iter end) {
+ char decimalPoint = getDecimalPoint();
+ if (decimalPoint == '\0' || decimalPoint == '.') {
+ return;
+ }
+ for (; begin != end; ++begin) {
+ if (*begin == '.') {
+ *begin = decimalPoint;
+ }
+ }
+}
+
+/**
+ * Return iterator that would be the new end of the range [begin,end), if we
+ * were to delete zeros in the end of string, but not the last zero before '.'.
+ */
+template <typename Iter>
+Iter fixZerosInTheEnd(Iter begin, Iter end, unsigned int precision) {
+ for (; begin != end; --end) {
+ if (*(end - 1) != '0') {
+ return end;
+ }
+ // Don't delete the last zero before the decimal point.
+ if (begin != (end - 1) && begin != (end - 2) && *(end - 2) == '.') {
+ if (precision) {
+ return end;
+ }
+ return end - 2;
+ }
+ }
+ return end;
+}
+
+} // namespace Json
#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
// End of content of file: src/lib_json/json_tool.h
// //////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: src/lib_json/json_reader.cpp
// //////////////////////////////////////////////////////////////////////
-// Copyright 2007-2010 Baptiste Lepilleur
+// Copyright 2007-2011 Baptiste Lepilleur and The JsonCpp Authors
+// Copyright (C) 2016 InfoTeCS JSC. All rights reserved.
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#if !defined(JSON_IS_AMALGAMATION)
-# include <json/reader.h>
-# include <json/value.h>
-# include "json_tool.h"
+#include "json_tool.h"
+#include <json/assertions.h>
+#include <json/reader.h>
+#include <json/value.h>
#endif // if !defined(JSON_IS_AMALGAMATION)
-#include <utility>
-#include <cstdio>
+#include <algorithm>
#include <cassert>
#include <cstring>
#include <iostream>
-#include <stdexcept>
+#include <istream>
+#include <limits>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <utility>
+
+#include <cstdio>
+#if __cplusplus >= 201103L
-#if _MSC_VER >= 1400 // VC++ 8.0
-#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated.
+#if !defined(sscanf)
+#define sscanf std::sscanf
#endif
-namespace Json {
+#endif //__cplusplus
-// Implementation of class Features
-// ////////////////////////////////
+#if defined(_MSC_VER)
+#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
+#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
+#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
+#endif //_MSC_VER
-Features::Features()
- : allowComments_( true )
- , strictRoot_( false )
-{
-}
+#if defined(_MSC_VER)
+// Disable warning about strdup being deprecated.
+#pragma warning(disable : 4996)
+#endif
+// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile
+// time to change the stack limit
+#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT)
+#define JSONCPP_DEPRECATED_STACK_LIMIT 1000
+#endif
-Features
-Features::all()
-{
- return Features();
-}
+static size_t const stackLimit_g =
+ JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue()
+namespace Json {
-Features
-Features::strictMode()
-{
- Features features;
- features.allowComments_ = false;
- features.strictRoot_ = true;
- return features;
-}
+#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
+using CharReaderPtr = std::unique_ptr<CharReader>;
+#else
+using CharReaderPtr = std::auto_ptr<CharReader>;
+#endif
-// Implementation of class Reader
+// Implementation of class Features
// ////////////////////////////////
+Features::Features() = default;
-static inline bool
-in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 )
-{
- return c == c1 || c == c2 || c == c3 || c == c4;
-}
+Features Features::all() { return {}; }
-static inline bool
-in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 )
-{
- return c == c1 || c == c2 || c == c3 || c == c4 || c == c5;
+Features Features::strictMode() {
+ Features features;
+ features.allowComments_ = false;
+ features.strictRoot_ = true;
+ features.allowDroppedNullPlaceholders_ = false;
+ features.allowNumericKeys_ = false;
+ return features;
}
+// Implementation of class Reader
+// ////////////////////////////////
-static bool
-containsNewLine( Reader::Location begin,
- Reader::Location end )
-{
- for ( ;begin < end; ++begin )
- if ( *begin == '\n' || *begin == '\r' )
- return true;
- return false;
+bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) {
+ return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
}
-
// Class Reader
// //////////////////////////////////////////////////////////////////
-Reader::Reader()
- : features_( Features::all() )
-{
-}
-
-
-Reader::Reader( const Features &features )
- : features_( features )
-{
-}
-
-
-bool
-Reader::parse( const std::string &document,
- Value &root,
- bool collectComments )
-{
- document_ = document;
- const char *begin = document_.c_str();
- const char *end = begin + document_.length();
- return parse( begin, end, root, collectComments );
-}
-
-
-bool
-Reader::parse( std::istream& sin,
- Value &root,
- bool collectComments )
-{
- //std::istream_iterator<char> begin(sin);
- //std::istream_iterator<char> end;
- // Those would allow streamed input from a file, if parse() were a
- // template function.
-
- // Since std::string is reference-counted, this at least does not
- // create an extra copy.
- std::string doc;
- std::getline(sin, doc, static_cast<char>(EOF));
- return parse( doc, root, collectComments );
-}
-
-bool
-Reader::parse( const char *beginDoc, const char *endDoc,
- Value &root,
- bool collectComments )
-{
- if ( !features_.allowComments_ )
- {
- collectComments = false;
- }
-
- begin_ = beginDoc;
- end_ = endDoc;
- collectComments_ = collectComments;
- current_ = begin_;
- lastValueEnd_ = nullptr;
- lastValue_ = nullptr;
- commentsBefore_ = "";
- errors_.clear();
- while ( !nodes_.empty() )
- nodes_.pop();
- nodes_.push( &root );
-
- bool successful = readValue();
- Token token;
- skipCommentTokens( token );
- if ( collectComments_ && !commentsBefore_.empty() )
- root.setComment( commentsBefore_, commentAfter );
- if ( features_.strictRoot_ )
- {
- if ( !root.isArray() && !root.isObject() )
- {
- // Set error location to start of doc, ideally should be first token found in doc
- token.type_ = tokenError;
- token.start_ = beginDoc;
- token.end_ = endDoc;
- addError( "A valid JSON document must be either an array or an object value.",
- token );
- return false;
- }
- }
- return successful;
-}
-
-
-bool
-Reader::readValue()
-{
- Token token;
- skipCommentTokens( token );
- bool successful = true;
-
- if ( collectComments_ && !commentsBefore_.empty() )
- {
- currentValue().setComment( commentsBefore_, commentBefore );
- commentsBefore_ = "";
- }
-
-
- switch ( token.type_ )
- {
- case tokenObjectBegin:
- successful = readObject( token );
- break;
- case tokenArrayBegin:
- successful = readArray( token );
- break;
- case tokenNumber:
- successful = decodeNumber( token );
- break;
- case tokenString:
- successful = decodeString( token );
- break;
- case tokenTrue:
- currentValue() = true;
- break;
- case tokenFalse:
- currentValue() = false;
+Reader::Reader() : features_(Features::all()) {}
+
+Reader::Reader(const Features& features) : features_(features) {}
+
+bool Reader::parse(const std::string& document, Value& root,
+ bool collectComments) {
+ document_.assign(document.begin(), document.end());
+ const char* begin = document_.c_str();
+ const char* end = begin + document_.length();
+ return parse(begin, end, root, collectComments);
+}
+
+bool Reader::parse(std::istream& is, Value& root, bool collectComments) {
+ // std::istream_iterator<char> begin(is);
+ // std::istream_iterator<char> end;
+ // Those would allow streamed input from a file, if parse() were a
+ // template function.
+
+ // Since String is reference-counted, this at least does not
+ // create an extra copy.
+ String doc(std::istreambuf_iterator<char>(is), {});
+ return parse(doc.data(), doc.data() + doc.size(), root, collectComments);
+}
+
+bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root,
+ bool collectComments) {
+ if (!features_.allowComments_) {
+ collectComments = false;
+ }
+
+ begin_ = beginDoc;
+ end_ = endDoc;
+ collectComments_ = collectComments;
+ current_ = begin_;
+ lastValueEnd_ = nullptr;
+ lastValue_ = nullptr;
+ commentsBefore_.clear();
+ errors_.clear();
+ while (!nodes_.empty())
+ nodes_.pop();
+ nodes_.push(&root);
+
+ bool successful = readValue();
+ Token token;
+ skipCommentTokens(token);
+ if (collectComments_ && !commentsBefore_.empty())
+ root.setComment(commentsBefore_, commentAfter);
+ if (features_.strictRoot_) {
+ if (!root.isArray() && !root.isObject()) {
+ // Set error location to start of doc, ideally should be first token found
+ // in doc
+ token.type_ = tokenError;
+ token.start_ = beginDoc;
+ token.end_ = endDoc;
+ addError(
+ "A valid JSON document must be either an array or an object value.",
+ token);
+ return false;
+ }
+ }
+ return successful;
+}
+
+bool Reader::readValue() {
+ // readValue() may call itself only if it calls readObject() or ReadArray().
+ // These methods execute nodes_.push() just before and nodes_.pop)() just
+ // after calling readValue(). parse() executes one nodes_.push(), so > instead
+ // of >=.
+ if (nodes_.size() > stackLimit_g)
+ throwRuntimeError("Exceeded stackLimit in readValue().");
+
+ Token token;
+ skipCommentTokens(token);
+ bool successful = true;
+
+ if (collectComments_ && !commentsBefore_.empty()) {
+ currentValue().setComment(commentsBefore_, commentBefore);
+ commentsBefore_.clear();
+ }
+
+ switch (token.type_) {
+ case tokenObjectBegin:
+ successful = readObject(token);
+ currentValue().setOffsetLimit(current_ - begin_);
+ break;
+ case tokenArrayBegin:
+ successful = readArray(token);
+ currentValue().setOffsetLimit(current_ - begin_);
+ break;
+ case tokenNumber:
+ successful = decodeNumber(token);
+ break;
+ case tokenString:
+ successful = decodeString(token);
+ break;
+ case tokenTrue: {
+ Value v(true);
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ } break;
+ case tokenFalse: {
+ Value v(false);
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ } break;
+ case tokenNull: {
+ Value v;
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ } break;
+ case tokenArraySeparator:
+ case tokenObjectEnd:
+ case tokenArrayEnd:
+ if (features_.allowDroppedNullPlaceholders_) {
+ // "Un-read" the current token and mark the current value as a null
+ // token.
+ current_--;
+ Value v;
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(current_ - begin_ - 1);
+ currentValue().setOffsetLimit(current_ - begin_);
break;
- case tokenNull:
- currentValue() = Value();
+ } // Else, fall through...
+ default:
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ return addError("Syntax error: value, object or array expected.", token);
+ }
+
+ if (collectComments_) {
+ lastValueEnd_ = current_;
+ lastValue_ = &currentValue();
+ }
+
+ return successful;
+}
+
+void Reader::skipCommentTokens(Token& token) {
+ if (features_.allowComments_) {
+ do {
+ readToken(token);
+ } while (token.type_ == tokenComment);
+ } else {
+ readToken(token);
+ }
+}
+
+bool Reader::readToken(Token& token) {
+ skipSpaces();
+ token.start_ = current_;
+ Char c = getNextChar();
+ bool ok = true;
+ switch (c) {
+ case '{':
+ token.type_ = tokenObjectBegin;
+ break;
+ case '}':
+ token.type_ = tokenObjectEnd;
+ break;
+ case '[':
+ token.type_ = tokenArrayBegin;
+ break;
+ case ']':
+ token.type_ = tokenArrayEnd;
+ break;
+ case '"':
+ token.type_ = tokenString;
+ ok = readString();
+ break;
+ case '/':
+ token.type_ = tokenComment;
+ ok = readComment();
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '-':
+ token.type_ = tokenNumber;
+ readNumber();
+ break;
+ case 't':
+ token.type_ = tokenTrue;
+ ok = match("rue", 3);
+ break;
+ case 'f':
+ token.type_ = tokenFalse;
+ ok = match("alse", 4);
+ break;
+ case 'n':
+ token.type_ = tokenNull;
+ ok = match("ull", 3);
+ break;
+ case ',':
+ token.type_ = tokenArraySeparator;
+ break;
+ case ':':
+ token.type_ = tokenMemberSeparator;
+ break;
+ case 0:
+ token.type_ = tokenEndOfStream;
+ break;
+ default:
+ ok = false;
+ break;
+ }
+ if (!ok)
+ token.type_ = tokenError;
+ token.end_ = current_;
+ return ok;
+}
+
+void Reader::skipSpaces() {
+ while (current_ != end_) {
+ Char c = *current_;
+ if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
+ ++current_;
+ else
break;
- default:
- return addError( "Syntax error: value, object or array expected.", token );
- }
-
- if ( collectComments_ )
- {
- lastValueEnd_ = current_;
- lastValue_ = &currentValue();
- }
-
- return successful;
+ }
}
-
-void
-Reader::skipCommentTokens( Token &token )
-{
- if ( features_.allowComments_ )
- {
- do
- {
- readToken( token );
- }
- while ( token.type_ == tokenComment );
- }
- else
- {
- readToken( token );
- }
-}
-
-
-bool
-Reader::expectToken( TokenType type, Token &token, const char *message )
-{
- readToken( token );
- if ( token.type_ != type )
- return addError( message, token );
- return true;
-}
-
-
-bool
-Reader::readToken( Token &token )
-{
- skipSpaces();
- token.start_ = current_;
- Char c = getNextChar();
- bool ok = true;
- switch ( c )
- {
- case '{':
- token.type_ = tokenObjectBegin;
- break;
- case '}':
- token.type_ = tokenObjectEnd;
- break;
- case '[':
- token.type_ = tokenArrayBegin;
- break;
- case ']':
- token.type_ = tokenArrayEnd;
- break;
- case '"':
- token.type_ = tokenString;
- ok = readString();
- break;
- case '/':
- token.type_ = tokenComment;
- ok = readComment();
+bool Reader::match(const Char* pattern, int patternLength) {
+ if (end_ - current_ < patternLength)
+ return false;
+ int index = patternLength;
+ while (index--)
+ if (current_[index] != pattern[index])
+ return false;
+ current_ += patternLength;
+ return true;
+}
+
+bool Reader::readComment() {
+ Location commentBegin = current_ - 1;
+ Char c = getNextChar();
+ bool successful = false;
+ if (c == '*')
+ successful = readCStyleComment();
+ else if (c == '/')
+ successful = readCppStyleComment();
+ if (!successful)
+ return false;
+
+ if (collectComments_) {
+ CommentPlacement placement = commentBefore;
+ if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
+ if (c != '*' || !containsNewLine(commentBegin, current_))
+ placement = commentAfterOnSameLine;
+ }
+
+ addComment(commentBegin, current_, placement);
+ }
+ return true;
+}
+
+String Reader::normalizeEOL(Reader::Location begin, Reader::Location end) {
+ String normalized;
+ normalized.reserve(static_cast<size_t>(end - begin));
+ Reader::Location current = begin;
+ while (current != end) {
+ char c = *current++;
+ if (c == '\r') {
+ if (current != end && *current == '\n')
+ // convert dos EOL
+ ++current;
+ // convert Mac EOL
+ normalized += '\n';
+ } else {
+ normalized += c;
+ }
+ }
+ return normalized;
+}
+
+void Reader::addComment(Location begin, Location end,
+ CommentPlacement placement) {
+ assert(collectComments_);
+ const String& normalized = normalizeEOL(begin, end);
+ if (placement == commentAfterOnSameLine) {
+ assert(lastValue_ != nullptr);
+ lastValue_->setComment(normalized, placement);
+ } else {
+ commentsBefore_ += normalized;
+ }
+}
+
+bool Reader::readCStyleComment() {
+ while ((current_ + 1) < end_) {
+ Char c = getNextChar();
+ if (c == '*' && *current_ == '/')
break;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- case '-':
- token.type_ = tokenNumber;
- readNumber();
+ }
+ return getNextChar() == '/';
+}
+
+bool Reader::readCppStyleComment() {
+ while (current_ != end_) {
+ Char c = getNextChar();
+ if (c == '\n')
break;
- case 't':
- token.type_ = tokenTrue;
- ok = match( "rue", 3 );
+ if (c == '\r') {
+ // Consume DOS EOL. It will be normalized in addComment.
+ if (current_ != end_ && *current_ == '\n')
+ getNextChar();
+ // Break on Moc OS 9 EOL.
break;
- case 'f':
- token.type_ = tokenFalse;
- ok = match( "alse", 4 );
+ }
+ }
+ return true;
+}
+
+void Reader::readNumber() {
+ Location p = current_;
+ char c = '0'; // stopgap for already consumed character
+ // integral part
+ while (c >= '0' && c <= '9')
+ c = (current_ = p) < end_ ? *p++ : '\0';
+ // fractional part
+ if (c == '.') {
+ c = (current_ = p) < end_ ? *p++ : '\0';
+ while (c >= '0' && c <= '9')
+ c = (current_ = p) < end_ ? *p++ : '\0';
+ }
+ // exponential part
+ if (c == 'e' || c == 'E') {
+ c = (current_ = p) < end_ ? *p++ : '\0';
+ if (c == '+' || c == '-')
+ c = (current_ = p) < end_ ? *p++ : '\0';
+ while (c >= '0' && c <= '9')
+ c = (current_ = p) < end_ ? *p++ : '\0';
+ }
+}
+
+bool Reader::readString() {
+ Char c = '\0';
+ while (current_ != end_) {
+ c = getNextChar();
+ if (c == '\\')
+ getNextChar();
+ else if (c == '"')
break;
- case 'n':
- token.type_ = tokenNull;
- ok = match( "ull", 3 );
+ }
+ return c == '"';
+}
+
+bool Reader::readObject(Token& token) {
+ Token tokenName;
+ String name;
+ Value init(objectValue);
+ currentValue().swapPayload(init);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ while (readToken(tokenName)) {
+ bool initialTokenOk = true;
+ while (tokenName.type_ == tokenComment && initialTokenOk)
+ initialTokenOk = readToken(tokenName);
+ if (!initialTokenOk)
break;
- case ',':
- token.type_ = tokenArraySeparator;
+ if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
+ return true;
+ name.clear();
+ if (tokenName.type_ == tokenString) {
+ if (!decodeString(tokenName, name))
+ return recoverFromError(tokenObjectEnd);
+ } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
+ Value numberName;
+ if (!decodeNumber(tokenName, numberName))
+ return recoverFromError(tokenObjectEnd);
+ name = numberName.asString();
+ } else {
break;
- case ':':
- token.type_ = tokenMemberSeparator;
+ }
+
+ Token colon;
+ if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
+ return addErrorAndRecover("Missing ':' after object member name", colon,
+ tokenObjectEnd);
+ }
+ Value& value = currentValue()[name];
+ nodes_.push(&value);
+ bool ok = readValue();
+ nodes_.pop();
+ if (!ok) // error already set
+ return recoverFromError(tokenObjectEnd);
+
+ Token comma;
+ if (!readToken(comma) ||
+ (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
+ comma.type_ != tokenComment)) {
+ return addErrorAndRecover("Missing ',' or '}' in object declaration",
+ comma, tokenObjectEnd);
+ }
+ bool finalizeTokenOk = true;
+ while (comma.type_ == tokenComment && finalizeTokenOk)
+ finalizeTokenOk = readToken(comma);
+ if (comma.type_ == tokenObjectEnd)
+ return true;
+ }
+ return addErrorAndRecover("Missing '}' or object member name", tokenName,
+ tokenObjectEnd);
+}
+
+bool Reader::readArray(Token& token) {
+ Value init(arrayValue);
+ currentValue().swapPayload(init);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ skipSpaces();
+ if (current_ != end_ && *current_ == ']') // empty array
+ {
+ Token endArray;
+ readToken(endArray);
+ return true;
+ }
+ int index = 0;
+ for (;;) {
+ Value& value = currentValue()[index++];
+ nodes_.push(&value);
+ bool ok = readValue();
+ nodes_.pop();
+ if (!ok) // error already set
+ return recoverFromError(tokenArrayEnd);
+
+ Token currentToken;
+ // Accept Comment after last item in the array.
+ ok = readToken(currentToken);
+ while (currentToken.type_ == tokenComment && ok) {
+ ok = readToken(currentToken);
+ }
+ bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
+ currentToken.type_ != tokenArrayEnd);
+ if (!ok || badTokenType) {
+ return addErrorAndRecover("Missing ',' or ']' in array declaration",
+ currentToken, tokenArrayEnd);
+ }
+ if (currentToken.type_ == tokenArrayEnd)
break;
- case 0:
- token.type_ = tokenEndOfStream;
+ }
+ return true;
+}
+
+bool Reader::decodeNumber(Token& token) {
+ Value decoded;
+ if (!decodeNumber(token, decoded))
+ return false;
+ currentValue().swapPayload(decoded);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ return true;
+}
+
+bool Reader::decodeNumber(Token& token, Value& decoded) {
+ // Attempts to parse the number as an integer. If the number is
+ // larger than the maximum supported value of an integer then
+ // we decode the number as a double.
+ Location current = token.start_;
+ bool isNegative = *current == '-';
+ if (isNegative)
+ ++current;
+ // TODO: Help the compiler do the div and mod at compile time or get rid of
+ // them.
+ Value::LargestUInt maxIntegerValue =
+ isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1
+ : Value::maxLargestUInt;
+ Value::LargestUInt threshold = maxIntegerValue / 10;
+ Value::LargestUInt value = 0;
+ while (current < token.end_) {
+ Char c = *current++;
+ if (c < '0' || c > '9')
+ return decodeDouble(token, decoded);
+ auto digit(static_cast<Value::UInt>(c - '0'));
+ if (value >= threshold) {
+ // We've hit or exceeded the max value divided by 10 (rounded down). If
+ // a) we've only just touched the limit, b) this is the last digit, and
+ // c) it's small enough to fit in that rounding delta, we're okay.
+ // Otherwise treat this number as a double to avoid overflow.
+ if (value > threshold || current != token.end_ ||
+ digit > maxIntegerValue % 10) {
+ return decodeDouble(token, decoded);
+ }
+ }
+ value = value * 10 + digit;
+ }
+ if (isNegative && value == maxIntegerValue)
+ decoded = Value::minLargestInt;
+ else if (isNegative)
+ decoded = -Value::LargestInt(value);
+ else if (value <= Value::LargestUInt(Value::maxInt))
+ decoded = Value::LargestInt(value);
+ else
+ decoded = value;
+ return true;
+}
+
+bool Reader::decodeDouble(Token& token) {
+ Value decoded;
+ if (!decodeDouble(token, decoded))
+ return false;
+ currentValue().swapPayload(decoded);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ return true;
+}
+
+bool Reader::decodeDouble(Token& token, Value& decoded) {
+ double value = 0;
+ String buffer(token.start_, token.end_);
+ IStringStream is(buffer);
+ if (!(is >> value))
+ return addError(
+ "'" + String(token.start_, token.end_) + "' is not a number.", token);
+ decoded = value;
+ return true;
+}
+
+bool Reader::decodeString(Token& token) {
+ String decoded_string;
+ if (!decodeString(token, decoded_string))
+ return false;
+ Value decoded(decoded_string);
+ currentValue().swapPayload(decoded);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ return true;
+}
+
+bool Reader::decodeString(Token& token, String& decoded) {
+ decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
+ Location current = token.start_ + 1; // skip '"'
+ Location end = token.end_ - 1; // do not include '"'
+ while (current != end) {
+ Char c = *current++;
+ if (c == '"')
break;
- default:
- ok = false;
+ if (c == '\\') {
+ if (current == end)
+ return addError("Empty escape sequence in string", token, current);
+ Char escape = *current++;
+ switch (escape) {
+ case '"':
+ decoded += '"';
+ break;
+ case '/':
+ decoded += '/';
+ break;
+ case '\\':
+ decoded += '\\';
+ break;
+ case 'b':
+ decoded += '\b';
+ break;
+ case 'f':
+ decoded += '\f';
+ break;
+ case 'n':
+ decoded += '\n';
+ break;
+ case 'r':
+ decoded += '\r';
+ break;
+ case 't':
+ decoded += '\t';
+ break;
+ case 'u': {
+ unsigned int unicode;
+ if (!decodeUnicodeCodePoint(token, current, end, unicode))
+ return false;
+ decoded += codePointToUTF8(unicode);
+ } break;
+ default:
+ return addError("Bad escape sequence in string", token, current);
+ }
+ } else {
+ decoded += c;
+ }
+ }
+ return true;
+}
+
+bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,
+ Location end, unsigned int& unicode) {
+
+ if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
+ return false;
+ if (unicode >= 0xD800 && unicode <= 0xDBFF) {
+ // surrogate pairs
+ if (end - current < 6)
+ return addError(
+ "additional six characters expected to parse unicode surrogate pair.",
+ token, current);
+ if (*(current++) == '\\' && *(current++) == 'u') {
+ unsigned int surrogatePair;
+ if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
+ unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
+ } else
+ return false;
+ } else
+ return addError("expecting another \\u token to begin the second half of "
+ "a unicode surrogate pair",
+ token, current);
+ }
+ return true;
+}
+
+bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current,
+ Location end,
+ unsigned int& ret_unicode) {
+ if (end - current < 4)
+ return addError(
+ "Bad unicode escape sequence in string: four digits expected.", token,
+ current);
+ int unicode = 0;
+ for (int index = 0; index < 4; ++index) {
+ Char c = *current++;
+ unicode *= 16;
+ if (c >= '0' && c <= '9')
+ unicode += c - '0';
+ else if (c >= 'a' && c <= 'f')
+ unicode += c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ unicode += c - 'A' + 10;
+ else
+ return addError(
+ "Bad unicode escape sequence in string: hexadecimal digit expected.",
+ token, current);
+ }
+ ret_unicode = static_cast<unsigned int>(unicode);
+ return true;
+}
+
+bool Reader::addError(const String& message, Token& token, Location extra) {
+ ErrorInfo info;
+ info.token_ = token;
+ info.message_ = message;
+ info.extra_ = extra;
+ errors_.push_back(info);
+ return false;
+}
+
+bool Reader::recoverFromError(TokenType skipUntilToken) {
+ size_t const errorCount = errors_.size();
+ Token skip;
+ for (;;) {
+ if (!readToken(skip))
+ errors_.resize(errorCount); // discard errors caused by recovery
+ if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
break;
- }
- if ( !ok )
- token.type_ = tokenError;
- token.end_ = current_;
- return true;
+ }
+ errors_.resize(errorCount);
+ return false;
+}
+
+bool Reader::addErrorAndRecover(const String& message, Token& token,
+ TokenType skipUntilToken) {
+ addError(message, token);
+ return recoverFromError(skipUntilToken);
+}
+
+Value& Reader::currentValue() { return *(nodes_.top()); }
+
+Reader::Char Reader::getNextChar() {
+ if (current_ == end_)
+ return 0;
+ return *current_++;
+}
+
+void Reader::getLocationLineAndColumn(Location location, int& line,
+ int& column) const {
+ Location current = begin_;
+ Location lastLineStart = current;
+ line = 0;
+ while (current < location && current != end_) {
+ Char c = *current++;
+ if (c == '\r') {
+ if (*current == '\n')
+ ++current;
+ lastLineStart = current;
+ ++line;
+ } else if (c == '\n') {
+ lastLineStart = current;
+ ++line;
+ }
+ }
+ // column & line start at 1
+ column = int(location - lastLineStart) + 1;
+ ++line;
+}
+
+String Reader::getLocationLineAndColumn(Location location) const {
+ int line, column;
+ getLocationLineAndColumn(location, line, column);
+ char buffer[18 + 16 + 16 + 1];
+ jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
+ return buffer;
}
+// Deprecated. Preserved for backward compatibility
+String Reader::getFormatedErrorMessages() const {
+ return getFormattedErrorMessages();
+}
+
+String Reader::getFormattedErrorMessages() const {
+ String formattedMessage;
+ for (const auto& error : errors_) {
+ formattedMessage +=
+ "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
+ formattedMessage += " " + error.message_ + "\n";
+ if (error.extra_)
+ formattedMessage +=
+ "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
+ }
+ return formattedMessage;
+}
+
+std::vector<Reader::StructuredError> Reader::getStructuredErrors() const {
+ std::vector<Reader::StructuredError> allErrors;
+ for (const auto& error : errors_) {
+ Reader::StructuredError structured;
+ structured.offset_start = error.token_.start_ - begin_;
+ structured.offset_limit = error.token_.end_ - begin_;
+ structured.message = error.message_;
+ allErrors.push_back(structured);
+ }
+ return allErrors;
+}
+
+bool Reader::pushError(const Value& value, const String& message) {
+ ptrdiff_t const length = end_ - begin_;
+ if (value.getOffsetStart() > length || value.getOffsetLimit() > length)
+ return false;
+ Token token;
+ token.type_ = tokenError;
+ token.start_ = begin_ + value.getOffsetStart();
+ token.end_ = begin_ + value.getOffsetLimit();
+ ErrorInfo info;
+ info.token_ = token;
+ info.message_ = message;
+ info.extra_ = nullptr;
+ errors_.push_back(info);
+ return true;
+}
+
+bool Reader::pushError(const Value& value, const String& message,
+ const Value& extra) {
+ ptrdiff_t const length = end_ - begin_;
+ if (value.getOffsetStart() > length || value.getOffsetLimit() > length ||
+ extra.getOffsetLimit() > length)
+ return false;
+ Token token;
+ token.type_ = tokenError;
+ token.start_ = begin_ + value.getOffsetStart();
+ token.end_ = begin_ + value.getOffsetLimit();
+ ErrorInfo info;
+ info.token_ = token;
+ info.message_ = message;
+ info.extra_ = begin_ + extra.getOffsetStart();
+ errors_.push_back(info);
+ return true;
+}
+
+bool Reader::good() const { return errors_.empty(); }
+
+// Originally copied from the Features class (now deprecated), used internally
+// for features implementation.
+class OurFeatures {
+public:
+ static OurFeatures all();
+ bool allowComments_;
+ bool allowTrailingCommas_;
+ bool strictRoot_;
+ bool allowDroppedNullPlaceholders_;
+ bool allowNumericKeys_;
+ bool allowSingleQuotes_;
+ bool failIfExtra_;
+ bool rejectDupKeys_;
+ bool allowSpecialFloats_;
+ bool skipBom_;
+ size_t stackLimit_;
+}; // OurFeatures
+
+OurFeatures OurFeatures::all() { return {}; }
-void
-Reader::skipSpaces()
-{
- while ( current_ != end_ )
- {
- Char c = *current_;
- if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' )
- ++current_;
- else
- break;
- }
-}
+// Implementation of class Reader
+// ////////////////////////////////
+// Originally copied from the Reader class (now deprecated), used internally
+// for implementing JSON reading.
+class OurReader {
+public:
+ using Char = char;
+ using Location = const Char*;
+ struct StructuredError {
+ ptrdiff_t offset_start;
+ ptrdiff_t offset_limit;
+ String message;
+ };
+
+ explicit OurReader(OurFeatures const& features);
+ bool parse(const char* beginDoc, const char* endDoc, Value& root,
+ bool collectComments = true);
+ String getFormattedErrorMessages() const;
+ std::vector<StructuredError> getStructuredErrors() const;
-bool
-Reader::match( Location pattern,
- int patternLength )
-{
- if ( end_ - current_ < patternLength )
- return false;
- int index = patternLength;
- while ( index-- )
- if ( current_[index] != pattern[index] )
- return false;
- current_ += patternLength;
- return true;
-}
-
-
-bool
-Reader::readComment()
-{
- Location commentBegin = current_ - 1;
- Char c = getNextChar();
- bool successful = false;
- if ( c == '*' )
- successful = readCStyleComment();
- else if ( c == '/' )
- successful = readCppStyleComment();
- if ( !successful )
+private:
+ OurReader(OurReader const&); // no impl
+ void operator=(OurReader const&); // no impl
+
+ enum TokenType {
+ tokenEndOfStream = 0,
+ tokenObjectBegin,
+ tokenObjectEnd,
+ tokenArrayBegin,
+ tokenArrayEnd,
+ tokenString,
+ tokenNumber,
+ tokenTrue,
+ tokenFalse,
+ tokenNull,
+ tokenNaN,
+ tokenPosInf,
+ tokenNegInf,
+ tokenArraySeparator,
+ tokenMemberSeparator,
+ tokenComment,
+ tokenError
+ };
+
+ class Token {
+ public:
+ TokenType type_;
+ Location start_;
+ Location end_;
+ };
+
+ class ErrorInfo {
+ public:
+ Token token_;
+ String message_;
+ Location extra_;
+ };
+
+ using Errors = std::deque<ErrorInfo>;
+
+ bool readToken(Token& token);
+ void skipSpaces();
+ void skipBom(bool skipBom);
+ bool match(const Char* pattern, int patternLength);
+ bool readComment();
+ bool readCStyleComment(bool* containsNewLineResult);
+ bool readCppStyleComment();
+ bool readString();
+ bool readStringSingleQuote();
+ bool readNumber(bool checkInf);
+ bool readValue();
+ bool readObject(Token& token);
+ bool readArray(Token& token);
+ bool decodeNumber(Token& token);
+ bool decodeNumber(Token& token, Value& decoded);
+ bool decodeString(Token& token);
+ bool decodeString(Token& token, String& decoded);
+ bool decodeDouble(Token& token);
+ bool decodeDouble(Token& token, Value& decoded);
+ bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
+ unsigned int& unicode);
+ bool decodeUnicodeEscapeSequence(Token& token, Location& current,
+ Location end, unsigned int& unicode);
+ bool addError(const String& message, Token& token, Location extra = nullptr);
+ bool recoverFromError(TokenType skipUntilToken);
+ bool addErrorAndRecover(const String& message, Token& token,
+ TokenType skipUntilToken);
+ void skipUntilSpace();
+ Value& currentValue();
+ Char getNextChar();
+ void getLocationLineAndColumn(Location location, int& line,
+ int& column) const;
+ String getLocationLineAndColumn(Location location) const;
+ void addComment(Location begin, Location end, CommentPlacement placement);
+ void skipCommentTokens(Token& token);
+
+ static String normalizeEOL(Location begin, Location end);
+ static bool containsNewLine(Location begin, Location end);
+
+ using Nodes = std::stack<Value*>;
+
+ Nodes nodes_{};
+ Errors errors_{};
+ String document_{};
+ Location begin_ = nullptr;
+ Location end_ = nullptr;
+ Location current_ = nullptr;
+ Location lastValueEnd_ = nullptr;
+ Value* lastValue_ = nullptr;
+ bool lastValueHasAComment_ = false;
+ String commentsBefore_{};
+
+ OurFeatures const features_;
+ bool collectComments_ = false;
+}; // OurReader
+
+// complete copy of Read impl, for OurReader
+
+bool OurReader::containsNewLine(OurReader::Location begin,
+ OurReader::Location end) {
+ return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
+}
+
+OurReader::OurReader(OurFeatures const& features) : features_(features) {}
+
+bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,
+ bool collectComments) {
+ if (!features_.allowComments_) {
+ collectComments = false;
+ }
+
+ begin_ = beginDoc;
+ end_ = endDoc;
+ collectComments_ = collectComments;
+ current_ = begin_;
+ lastValueEnd_ = nullptr;
+ lastValue_ = nullptr;
+ commentsBefore_.clear();
+ errors_.clear();
+ while (!nodes_.empty())
+ nodes_.pop();
+ nodes_.push(&root);
+
+ // skip byte order mark if it exists at the beginning of the UTF-8 text.
+ skipBom(features_.skipBom_);
+ bool successful = readValue();
+ nodes_.pop();
+ Token token;
+ skipCommentTokens(token);
+ if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) {
+ addError("Extra non-whitespace after JSON value.", token);
+ return false;
+ }
+ if (collectComments_ && !commentsBefore_.empty())
+ root.setComment(commentsBefore_, commentAfter);
+ if (features_.strictRoot_) {
+ if (!root.isArray() && !root.isObject()) {
+ // Set error location to start of doc, ideally should be first token found
+ // in doc
+ token.type_ = tokenError;
+ token.start_ = beginDoc;
+ token.end_ = endDoc;
+ addError(
+ "A valid JSON document must be either an array or an object value.",
+ token);
return false;
-
- if ( collectComments_ )
- {
- CommentPlacement placement = commentBefore;
- if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) )
- {
- if ( c != '*' || !containsNewLine( commentBegin, current_ ) )
- placement = commentAfterOnSameLine;
- }
-
- addComment( commentBegin, current_, placement );
- }
- return true;
-}
-
-
-void
-Reader::addComment( Location begin,
- Location end,
- CommentPlacement placement )
-{
- assert( collectComments_ );
- if ( placement == commentAfterOnSameLine )
- {
- assert( lastValue_ != nullptr );
- lastValue_->setComment( std::string( begin, end ), placement );
- }
- else
- {
- if ( !commentsBefore_.empty() )
- commentsBefore_ += "\n";
- commentsBefore_ += std::string( begin, end );
- }
-}
-
-
-bool
-Reader::readCStyleComment()
-{
- while ( current_ != end_ )
- {
- Char c = getNextChar();
- if ( c == '*' && *current_ == '/' )
- break;
- }
- return getNextChar() == '/';
-}
-
-
-bool
-Reader::readCppStyleComment()
-{
- while ( current_ != end_ )
- {
- Char c = getNextChar();
- if ( c == '\r' || c == '\n' )
- break;
- }
- return true;
-}
-
-
-void
-Reader::readNumber()
-{
- while ( current_ != end_ )
- {
- if ( !(*current_ >= '0' && *current_ <= '9') &&
- !in( *current_, '.', 'e', 'E', '+', '-' ) )
- break;
+ }
+ }
+ return successful;
+}
+
+bool OurReader::readValue() {
+ // To preserve the old behaviour we cast size_t to int.
+ if (nodes_.size() > features_.stackLimit_)
+ throwRuntimeError("Exceeded stackLimit in readValue().");
+ Token token;
+ skipCommentTokens(token);
+ bool successful = true;
+
+ if (collectComments_ && !commentsBefore_.empty()) {
+ currentValue().setComment(commentsBefore_, commentBefore);
+ commentsBefore_.clear();
+ }
+
+ switch (token.type_) {
+ case tokenObjectBegin:
+ successful = readObject(token);
+ currentValue().setOffsetLimit(current_ - begin_);
+ break;
+ case tokenArrayBegin:
+ successful = readArray(token);
+ currentValue().setOffsetLimit(current_ - begin_);
+ break;
+ case tokenNumber:
+ successful = decodeNumber(token);
+ break;
+ case tokenString:
+ successful = decodeString(token);
+ break;
+ case tokenTrue: {
+ Value v(true);
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ } break;
+ case tokenFalse: {
+ Value v(false);
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ } break;
+ case tokenNull: {
+ Value v;
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ } break;
+ case tokenNaN: {
+ Value v(std::numeric_limits<double>::quiet_NaN());
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ } break;
+ case tokenPosInf: {
+ Value v(std::numeric_limits<double>::infinity());
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ } break;
+ case tokenNegInf: {
+ Value v(-std::numeric_limits<double>::infinity());
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ } break;
+ case tokenArraySeparator:
+ case tokenObjectEnd:
+ case tokenArrayEnd:
+ if (features_.allowDroppedNullPlaceholders_) {
+ // "Un-read" the current token and mark the current value as a null
+ // token.
+ current_--;
+ Value v;
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(current_ - begin_ - 1);
+ currentValue().setOffsetLimit(current_ - begin_);
+ break;
+ } // else, fall through ...
+ default:
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ return addError("Syntax error: value, object or array expected.", token);
+ }
+
+ if (collectComments_) {
+ lastValueEnd_ = current_;
+ lastValueHasAComment_ = false;
+ lastValue_ = &currentValue();
+ }
+
+ return successful;
+}
+
+void OurReader::skipCommentTokens(Token& token) {
+ if (features_.allowComments_) {
+ do {
+ readToken(token);
+ } while (token.type_ == tokenComment);
+ } else {
+ readToken(token);
+ }
+}
+
+bool OurReader::readToken(Token& token) {
+ skipSpaces();
+ token.start_ = current_;
+ Char c = getNextChar();
+ bool ok = true;
+ switch (c) {
+ case '{':
+ token.type_ = tokenObjectBegin;
+ break;
+ case '}':
+ token.type_ = tokenObjectEnd;
+ break;
+ case '[':
+ token.type_ = tokenArrayBegin;
+ break;
+ case ']':
+ token.type_ = tokenArrayEnd;
+ break;
+ case '"':
+ token.type_ = tokenString;
+ ok = readString();
+ break;
+ case '\'':
+ if (features_.allowSingleQuotes_) {
+ token.type_ = tokenString;
+ ok = readStringSingleQuote();
+ } else {
+ // If we don't allow single quotes, this is a failure case.
+ ok = false;
+ }
+ break;
+ case '/':
+ token.type_ = tokenComment;
+ ok = readComment();
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ token.type_ = tokenNumber;
+ readNumber(false);
+ break;
+ case '-':
+ if (readNumber(true)) {
+ token.type_ = tokenNumber;
+ } else {
+ token.type_ = tokenNegInf;
+ ok = features_.allowSpecialFloats_ && match("nfinity", 7);
+ }
+ break;
+ case '+':
+ if (readNumber(true)) {
+ token.type_ = tokenNumber;
+ } else {
+ token.type_ = tokenPosInf;
+ ok = features_.allowSpecialFloats_ && match("nfinity", 7);
+ }
+ break;
+ case 't':
+ token.type_ = tokenTrue;
+ ok = match("rue", 3);
+ break;
+ case 'f':
+ token.type_ = tokenFalse;
+ ok = match("alse", 4);
+ break;
+ case 'n':
+ token.type_ = tokenNull;
+ ok = match("ull", 3);
+ break;
+ case 'N':
+ if (features_.allowSpecialFloats_) {
+ token.type_ = tokenNaN;
+ ok = match("aN", 2);
+ } else {
+ ok = false;
+ }
+ break;
+ case 'I':
+ if (features_.allowSpecialFloats_) {
+ token.type_ = tokenPosInf;
+ ok = match("nfinity", 7);
+ } else {
+ ok = false;
+ }
+ break;
+ case ',':
+ token.type_ = tokenArraySeparator;
+ break;
+ case ':':
+ token.type_ = tokenMemberSeparator;
+ break;
+ case 0:
+ token.type_ = tokenEndOfStream;
+ break;
+ default:
+ ok = false;
+ break;
+ }
+ if (!ok)
+ token.type_ = tokenError;
+ token.end_ = current_;
+ return ok;
+}
+
+void OurReader::skipSpaces() {
+ while (current_ != end_) {
+ Char c = *current_;
+ if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
++current_;
- }
-}
-
-bool
-Reader::readString()
-{
- Char c = 0;
- while ( current_ != end_ )
- {
- c = getNextChar();
- if ( c == '\\' )
- getNextChar();
- else if ( c == '"' )
- break;
- }
- return c == '"';
-}
-
-
-bool
-Reader::readObject( Token &/*tokenStart*/ )
-{
- Token tokenName;
- std::string name;
- currentValue() = Value( objectValue );
- while ( readToken( tokenName ) )
- {
- bool initialTokenOk = true;
- while ( tokenName.type_ == tokenComment && initialTokenOk )
- initialTokenOk = readToken( tokenName );
- if ( !initialTokenOk )
- break;
- if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object
- return true;
- if ( tokenName.type_ != tokenString )
- break;
-
- name = "";
- if ( !decodeString( tokenName, name ) )
- return recoverFromError( tokenObjectEnd );
-
- Token colon;
- if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator )
- {
- return addErrorAndRecover( "Missing ':' after object member name",
- colon,
- tokenObjectEnd );
- }
- Value &value = currentValue()[ name ];
- nodes_.push( &value );
- bool ok = readValue();
- nodes_.pop();
- if ( !ok ) // error already set
- return recoverFromError( tokenObjectEnd );
-
- Token comma;
- if ( !readToken( comma )
- || ( comma.type_ != tokenObjectEnd &&
- comma.type_ != tokenArraySeparator &&
- comma.type_ != tokenComment ) )
- {
- return addErrorAndRecover( "Missing ',' or '}' in object declaration",
- comma,
- tokenObjectEnd );
- }
- bool finalizeTokenOk = true;
- while ( comma.type_ == tokenComment &&
- finalizeTokenOk )
- finalizeTokenOk = readToken( comma );
- if ( comma.type_ == tokenObjectEnd )
- return true;
- }
- return addErrorAndRecover( "Missing '}' or object member name",
- tokenName,
- tokenObjectEnd );
-}
-
-
-bool
-Reader::readArray( Token &/*tokenStart*/ )
-{
- currentValue() = Value( arrayValue );
- skipSpaces();
- if ( *current_ == ']' ) // empty array
- {
- Token endArray;
- readToken( endArray );
- return true;
- }
- int index = 0;
- for (;;)
- {
- Value &value = currentValue()[ index++ ];
- nodes_.push( &value );
- bool ok = readValue();
- nodes_.pop();
- if ( !ok ) // error already set
- return recoverFromError( tokenArrayEnd );
-
- Token token;
- // Accept Comment after last item in the array.
- ok = readToken( token );
- while ( token.type_ == tokenComment && ok )
- {
- ok = readToken( token );
- }
- bool badTokenType = ( token.type_ != tokenArraySeparator &&
- token.type_ != tokenArrayEnd );
- if ( !ok || badTokenType )
- {
- return addErrorAndRecover( "Missing ',' or ']' in array declaration",
- token,
- tokenArrayEnd );
- }
- if ( token.type_ == tokenArrayEnd )
- break;
- }
- return true;
-}
-
-
-bool
-Reader::decodeNumber( Token &token )
-{
- bool isDouble = false;
- for ( Location inspect = token.start_; inspect != token.end_; ++inspect )
- {
- isDouble = isDouble
- || in( *inspect, '.', 'e', 'E', '+' )
- || ( *inspect == '-' && inspect != token.start_ );
- }
- if ( isDouble )
- return decodeDouble( token );
- // Attempts to parse the number as an integer. If the number is
- // larger than the maximum supported value of an integer then
- // we decode the number as a double.
- Location current = token.start_;
- bool isNegative = *current == '-';
- if ( isNegative )
- ++current;
- Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(-Value::minLargestInt)
- : Value::maxLargestUInt;
- Value::LargestUInt threshold = maxIntegerValue / 10;
- Value::UInt lastDigitThreshold = Value::UInt( maxIntegerValue % 10 );
- assert(/* lastDigitThreshold >=0 &&*/ lastDigitThreshold <= 9 );
- Value::LargestUInt value = 0;
- while ( current < token.end_ )
- {
- Char c = *current++;
- if ( c < '0' || c > '9' )
- return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
- Value::UInt digit(c - '0');
- if ( value >= threshold )
- {
- // If the current digit is not the last one, or if it is
- // greater than the last digit of the maximum integer value,
- // the parse the number as a double.
- if ( current != token.end_ || digit > lastDigitThreshold )
- {
- return decodeDouble( token );
- }
- }
- value = value * 10 + digit;
- }
- if ( isNegative )
- currentValue() = -Value::LargestInt( value );
- else if ( value <= Value::LargestUInt(Value::maxInt) )
- currentValue() = Value::LargestInt( value );
- else
- currentValue() = value;
- return true;
-}
-
-
-bool
-Reader::decodeDouble( Token &token )
-{
- double value = 0;
- const int bufferSize = 32;
- int count;
- int length = int(token.end_ - token.start_);
- if ( length <= bufferSize )
- {
- Char buffer[bufferSize+1];
- memcpy( buffer, token.start_, length );
- buffer[length] = 0;
- count = sscanf( buffer, "%lf", &value );
- }
- else
- {
- std::string buffer( token.start_, token.end_ );
- count = sscanf( buffer.c_str(), "%lf", &value );
- }
-
- if ( count != 1 )
- return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
- currentValue() = value;
- return true;
-}
-
-
-bool
-Reader::decodeString( Token &token )
-{
- std::string decoded;
- if ( !decodeString( token, decoded ) )
- return false;
- currentValue() = decoded;
- return true;
-}
-
-
-bool
-Reader::decodeString( Token &token, std::string &decoded )
-{
- decoded.reserve( token.end_ - token.start_ - 2 );
- Location current = token.start_ + 1; // skip '"'
- Location end = token.end_ - 1; // do not include '"'
- while ( current != end )
- {
- Char c = *current++;
- if ( c == '"' )
- break;
- else if ( c == '\\' )
- {
- if ( current == end )
- return addError( "Empty escape sequence in string", token, current );
- Char escape = *current++;
- switch ( escape )
- {
- case '"': decoded += '"'; break;
- case '/': decoded += '/'; break;
- case '\\': decoded += '\\'; break;
- case 'b': decoded += '\b'; break;
- case 'f': decoded += '\f'; break;
- case 'n': decoded += '\n'; break;
- case 'r': decoded += '\r'; break;
- case 't': decoded += '\t'; break;
- case 'u':
- {
- unsigned int unicode;
- if ( !decodeUnicodeCodePoint( token, current, end, unicode ) )
- return false;
- decoded += codePointToUTF8(unicode);
- }
- break;
- default:
- return addError( "Bad escape sequence in string", token, current );
- }
- }
- else
- {
- decoded += c;
- }
- }
- return true;
+ else
+ break;
+ }
}
-bool
-Reader::decodeUnicodeCodePoint( Token &token,
- Location &current,
- Location end,
- unsigned int &unicode )
-{
+void OurReader::skipBom(bool skipBom) {
+ // The default behavior is to skip BOM.
+ if (skipBom) {
+ if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) {
+ begin_ += 3;
+ current_ = begin_;
+ }
+ }
+}
- if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) )
+bool OurReader::match(const Char* pattern, int patternLength) {
+ if (end_ - current_ < patternLength)
+ return false;
+ int index = patternLength;
+ while (index--)
+ if (current_[index] != pattern[index])
return false;
- if (unicode >= 0xD800 && unicode <= 0xDBFF)
- {
- // surrogate pairs
- if (end - current < 6)
- return addError( "additional six characters expected to parse unicode surrogate pair.", token, current );
- unsigned int surrogatePair;
- if (*(current++) == '\\' && *(current++)== 'u')
- {
- if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair ))
- {
- unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
- }
- else
- return false;
- }
- else
- return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current );
- }
- return true;
-}
-
-bool
-Reader::decodeUnicodeEscapeSequence( Token &token,
- Location &current,
- Location end,
- unsigned int &unicode )
-{
- if ( end - current < 4 )
- return addError( "Bad unicode escape sequence in string: four digits expected.", token, current );
- unicode = 0;
- for ( int index =0; index < 4; ++index )
- {
- Char c = *current++;
- unicode *= 16;
- if ( c >= '0' && c <= '9' )
- unicode += c - '0';
- else if ( c >= 'a' && c <= 'f' )
- unicode += c - 'a' + 10;
- else if ( c >= 'A' && c <= 'F' )
- unicode += c - 'A' + 10;
- else
- return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current );
- }
- return true;
-}
-
-
-bool
-Reader::addError( const std::string &message,
- Token &token,
- Location extra )
-{
- ErrorInfo info;
- info.token_ = token;
- info.message_ = message;
- info.extra_ = extra;
- errors_.push_back( info );
- return false;
-}
-
-
-bool
-Reader::recoverFromError( TokenType skipUntilToken )
-{
- int errorCount = int(errors_.size());
- Token skip;
- for (;;)
- {
- if ( !readToken(skip) )
- errors_.resize( errorCount ); // discard errors caused by recovery
- if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream )
- break;
- }
- errors_.resize( errorCount );
- return false;
-}
-
-
-bool
-Reader::addErrorAndRecover( const std::string &message,
- Token &token,
- TokenType skipUntilToken )
-{
- addError( message, token );
- return recoverFromError( skipUntilToken );
-}
-
-
-Value &
-Reader::currentValue()
-{
- return *(nodes_.top());
-}
-
-
-Reader::Char
-Reader::getNextChar()
-{
- if ( current_ == end_ )
- return 0;
- return *current_++;
-}
-
-
-void
-Reader::getLocationLineAndColumn( Location location,
- int &line,
- int &column ) const
-{
- Location current = begin_;
- Location lastLineStart = current;
- line = 0;
- while ( current < location && current != end_ )
- {
- Char c = *current++;
- if ( c == '\r' )
- {
- if ( *current == '\n' )
- ++current;
- lastLineStart = current;
- ++line;
+ current_ += patternLength;
+ return true;
+}
+
+bool OurReader::readComment() {
+ const Location commentBegin = current_ - 1;
+ const Char c = getNextChar();
+ bool successful = false;
+ bool cStyleWithEmbeddedNewline = false;
+
+ const bool isCStyleComment = (c == '*');
+ const bool isCppStyleComment = (c == '/');
+ if (isCStyleComment) {
+ successful = readCStyleComment(&cStyleWithEmbeddedNewline);
+ } else if (isCppStyleComment) {
+ successful = readCppStyleComment();
+ }
+
+ if (!successful)
+ return false;
+
+ if (collectComments_) {
+ CommentPlacement placement = commentBefore;
+
+ if (!lastValueHasAComment_) {
+ if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
+ if (isCppStyleComment || !cStyleWithEmbeddedNewline) {
+ placement = commentAfterOnSameLine;
+ lastValueHasAComment_ = true;
+ }
}
- else if ( c == '\n' )
- {
- lastLineStart = current;
- ++line;
- }
- }
- // column & line start at 1
- column = int(location - lastLineStart) + 1;
- ++line;
-}
-
-
-std::string
-Reader::getLocationLineAndColumn( Location location ) const
-{
- int line, column;
- getLocationLineAndColumn( location, line, column );
- char buffer[18+16+16+1];
- sprintf( buffer, "Line %d, Column %d", line, column );
- return buffer;
-}
-
-
-// Deprecated. Preserved for backward compatibility
-std::string
-Reader::getFormatedErrorMessages() const
-{
- return getFormattedErrorMessages();
-}
-
+ }
+
+ addComment(commentBegin, current_, placement);
+ }
+ return true;
+}
+
+String OurReader::normalizeEOL(OurReader::Location begin,
+ OurReader::Location end) {
+ String normalized;
+ normalized.reserve(static_cast<size_t>(end - begin));
+ OurReader::Location current = begin;
+ while (current != end) {
+ char c = *current++;
+ if (c == '\r') {
+ if (current != end && *current == '\n')
+ // convert dos EOL
+ ++current;
+ // convert Mac EOL
+ normalized += '\n';
+ } else {
+ normalized += c;
+ }
+ }
+ return normalized;
+}
+
+void OurReader::addComment(Location begin, Location end,
+ CommentPlacement placement) {
+ assert(collectComments_);
+ const String& normalized = normalizeEOL(begin, end);
+ if (placement == commentAfterOnSameLine) {
+ assert(lastValue_ != nullptr);
+ lastValue_->setComment(normalized, placement);
+ } else {
+ commentsBefore_ += normalized;
+ }
+}
+
+bool OurReader::readCStyleComment(bool* containsNewLineResult) {
+ *containsNewLineResult = false;
+
+ while ((current_ + 1) < end_) {
+ Char c = getNextChar();
+ if (c == '*' && *current_ == '/')
+ break;
+ if (c == '\n')
+ *containsNewLineResult = true;
+ }
-std::string
-Reader::getFormattedErrorMessages() const
-{
- std::string formattedMessage;
- for ( Errors::const_iterator itError = errors_.begin();
- itError != errors_.end();
- ++itError )
- {
- const ErrorInfo &error = *itError;
- formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n";
- formattedMessage += " " + error.message_ + "\n";
- if ( error.extra_ )
- formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n";
- }
- return formattedMessage;
+ return getNextChar() == '/';
}
-
-std::istream& operator>>( std::istream &sin, Value &root )
-{
- Json::Reader reader;
- bool ok = reader.parse(sin, root, true);
- //JSON_ASSERT( ok );
- if (!ok) throw std::runtime_error(reader.getFormattedErrorMessages());
- return sin;
+bool OurReader::readCppStyleComment() {
+ while (current_ != end_) {
+ Char c = getNextChar();
+ if (c == '\n')
+ break;
+ if (c == '\r') {
+ // Consume DOS EOL. It will be normalized in addComment.
+ if (current_ != end_ && *current_ == '\n')
+ getNextChar();
+ // Break on Moc OS 9 EOL.
+ break;
+ }
+ }
+ return true;
+}
+
+bool OurReader::readNumber(bool checkInf) {
+ Location p = current_;
+ if (checkInf && p != end_ && *p == 'I') {
+ current_ = ++p;
+ return false;
+ }
+ char c = '0'; // stopgap for already consumed character
+ // integral part
+ while (c >= '0' && c <= '9')
+ c = (current_ = p) < end_ ? *p++ : '\0';
+ // fractional part
+ if (c == '.') {
+ c = (current_ = p) < end_ ? *p++ : '\0';
+ while (c >= '0' && c <= '9')
+ c = (current_ = p) < end_ ? *p++ : '\0';
+ }
+ // exponential part
+ if (c == 'e' || c == 'E') {
+ c = (current_ = p) < end_ ? *p++ : '\0';
+ if (c == '+' || c == '-')
+ c = (current_ = p) < end_ ? *p++ : '\0';
+ while (c >= '0' && c <= '9')
+ c = (current_ = p) < end_ ? *p++ : '\0';
+ }
+ return true;
+}
+bool OurReader::readString() {
+ Char c = 0;
+ while (current_ != end_) {
+ c = getNextChar();
+ if (c == '\\')
+ getNextChar();
+ else if (c == '"')
+ break;
+ }
+ return c == '"';
}
-
-} // namespace Json
-
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: src/lib_json/json_reader.cpp
-// //////////////////////////////////////////////////////////////////////
-
-
-
-
-
-
-// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: src/lib_json/json_batchallocator.h
-// //////////////////////////////////////////////////////////////////////
-
-// Copyright 2007-2010 Baptiste Lepilleur
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef JSONCPP_BATCHALLOCATOR_H_INCLUDED
-# define JSONCPP_BATCHALLOCATOR_H_INCLUDED
-
-# include <stdlib.h>
-# include <assert.h>
-
-# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
-
-namespace Json {
-
-/* Fast memory allocator.
- *
- * This memory allocator allocates memory for a batch of object (specified by
- * the page size, the number of object in each page).
- *
- * It does not allow the destruction of a single object. All the allocated objects
- * can be destroyed at once. The memory can be either released or reused for future
- * allocation.
- *
- * The in-place new operator must be used to construct the object using the pointer
- * returned by allocate.
- */
-template<typename AllocatedType
- ,const unsigned int objectPerAllocation>
-class BatchAllocator
-{
-public:
- typedef AllocatedType Type;
-
- BatchAllocator( unsigned int objectsPerPage = 255 )
- : freeHead_( 0 )
- , objectsPerPage_( objectsPerPage )
- {
-// printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() );
- assert( sizeof(AllocatedType) * objectPerAllocation >= sizeof(AllocatedType *) ); // We must be able to store a slist in the object free space.
- assert( objectsPerPage >= 16 );
- batches_ = allocateBatch( 0 ); // allocated a dummy page
- currentBatch_ = batches_;
- }
-
- ~BatchAllocator()
- {
- for ( BatchInfo *batch = batches_; batch; )
- {
- BatchInfo *nextBatch = batch->next_;
- free( batch );
- batch = nextBatch;
- }
- }
-
- /// allocate space for an array of objectPerAllocation object.
- /// @warning it is the responsability of the caller to call objects constructors.
- AllocatedType *allocate()
- {
- if ( freeHead_ ) // returns node from free list.
- {
- AllocatedType *object = freeHead_;
- freeHead_ = *static_cast<AllocatedType **>(object);
- return object;
+bool OurReader::readStringSingleQuote() {
+ Char c = 0;
+ while (current_ != end_) {
+ c = getNextChar();
+ if (c == '\\')
+ getNextChar();
+ else if (c == '\'')
+ break;
+ }
+ return c == '\'';
+}
+
+bool OurReader::readObject(Token& token) {
+ Token tokenName;
+ String name;
+ Value init(objectValue);
+ currentValue().swapPayload(init);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ while (readToken(tokenName)) {
+ bool initialTokenOk = true;
+ while (tokenName.type_ == tokenComment && initialTokenOk)
+ initialTokenOk = readToken(tokenName);
+ if (!initialTokenOk)
+ break;
+ if (tokenName.type_ == tokenObjectEnd &&
+ (name.empty() ||
+ features_.allowTrailingCommas_)) // empty object or trailing comma
+ return true;
+ name.clear();
+ if (tokenName.type_ == tokenString) {
+ if (!decodeString(tokenName, name))
+ return recoverFromError(tokenObjectEnd);
+ } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
+ Value numberName;
+ if (!decodeNumber(tokenName, numberName))
+ return recoverFromError(tokenObjectEnd);
+ name = numberName.asString();
+ } else {
+ break;
+ }
+ if (name.length() >= (1U << 30))
+ throwRuntimeError("keylength >= 2^30");
+ if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
+ String msg = "Duplicate key: '" + name + "'";
+ return addErrorAndRecover(msg, tokenName, tokenObjectEnd);
+ }
+
+ Token colon;
+ if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
+ return addErrorAndRecover("Missing ':' after object member name", colon,
+ tokenObjectEnd);
+ }
+ Value& value = currentValue()[name];
+ nodes_.push(&value);
+ bool ok = readValue();
+ nodes_.pop();
+ if (!ok) // error already set
+ return recoverFromError(tokenObjectEnd);
+
+ Token comma;
+ if (!readToken(comma) ||
+ (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
+ comma.type_ != tokenComment)) {
+ return addErrorAndRecover("Missing ',' or '}' in object declaration",
+ comma, tokenObjectEnd);
+ }
+ bool finalizeTokenOk = true;
+ while (comma.type_ == tokenComment && finalizeTokenOk)
+ finalizeTokenOk = readToken(comma);
+ if (comma.type_ == tokenObjectEnd)
+ return true;
+ }
+ return addErrorAndRecover("Missing '}' or object member name", tokenName,
+ tokenObjectEnd);
+}
+
+bool OurReader::readArray(Token& token) {
+ Value init(arrayValue);
+ currentValue().swapPayload(init);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ int index = 0;
+ for (;;) {
+ skipSpaces();
+ if (current_ != end_ && *current_ == ']' &&
+ (index == 0 ||
+ (features_.allowTrailingCommas_ &&
+ !features_.allowDroppedNullPlaceholders_))) // empty array or trailing
+ // comma
+ {
+ Token endArray;
+ readToken(endArray);
+ return true;
+ }
+ Value& value = currentValue()[index++];
+ nodes_.push(&value);
+ bool ok = readValue();
+ nodes_.pop();
+ if (!ok) // error already set
+ return recoverFromError(tokenArrayEnd);
+
+ Token currentToken;
+ // Accept Comment after last item in the array.
+ ok = readToken(currentToken);
+ while (currentToken.type_ == tokenComment && ok) {
+ ok = readToken(currentToken);
+ }
+ bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
+ currentToken.type_ != tokenArrayEnd);
+ if (!ok || badTokenType) {
+ return addErrorAndRecover("Missing ',' or ']' in array declaration",
+ currentToken, tokenArrayEnd);
+ }
+ if (currentToken.type_ == tokenArrayEnd)
+ break;
+ }
+ return true;
+}
+
+bool OurReader::decodeNumber(Token& token) {
+ Value decoded;
+ if (!decodeNumber(token, decoded))
+ return false;
+ currentValue().swapPayload(decoded);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ return true;
+}
+
+bool OurReader::decodeNumber(Token& token, Value& decoded) {
+ // Attempts to parse the number as an integer. If the number is
+ // larger than the maximum supported value of an integer then
+ // we decode the number as a double.
+ Location current = token.start_;
+ const bool isNegative = *current == '-';
+ if (isNegative) {
+ ++current;
+ }
+
+ // We assume we can represent the largest and smallest integer types as
+ // unsigned integers with separate sign. This is only true if they can fit
+ // into an unsigned integer.
+ static_assert(Value::maxLargestInt <= Value::maxLargestUInt,
+ "Int must be smaller than UInt");
+
+ // We need to convert minLargestInt into a positive number. The easiest way
+ // to do this conversion is to assume our "threshold" value of minLargestInt
+ // divided by 10 can fit in maxLargestInt when absolute valued. This should
+ // be a safe assumption.
+ static_assert(Value::minLargestInt <= -Value::maxLargestInt,
+ "The absolute value of minLargestInt must be greater than or "
+ "equal to maxLargestInt");
+ static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt,
+ "The absolute value of minLargestInt must be only 1 magnitude "
+ "larger than maxLargest Int");
+
+ static constexpr Value::LargestUInt positive_threshold =
+ Value::maxLargestUInt / 10;
+ static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10;
+
+ // For the negative values, we have to be more careful. Since typically
+ // -Value::minLargestInt will cause an overflow, we first divide by 10 and
+ // then take the inverse. This assumes that minLargestInt is only a single
+ // power of 10 different in magnitude, which we check above. For the last
+ // digit, we take the modulus before negating for the same reason.
+ static constexpr auto negative_threshold =
+ Value::LargestUInt(-(Value::minLargestInt / 10));
+ static constexpr auto negative_last_digit =
+ Value::UInt(-(Value::minLargestInt % 10));
+
+ const Value::LargestUInt threshold =
+ isNegative ? negative_threshold : positive_threshold;
+ const Value::UInt max_last_digit =
+ isNegative ? negative_last_digit : positive_last_digit;
+
+ Value::LargestUInt value = 0;
+ while (current < token.end_) {
+ Char c = *current++;
+ if (c < '0' || c > '9')
+ return decodeDouble(token, decoded);
+
+ const auto digit(static_cast<Value::UInt>(c - '0'));
+ if (value >= threshold) {
+ // We've hit or exceeded the max value divided by 10 (rounded down). If
+ // a) we've only just touched the limit, meaing value == threshold,
+ // b) this is the last digit, or
+ // c) it's small enough to fit in that rounding delta, we're okay.
+ // Otherwise treat this number as a double to avoid overflow.
+ if (value > threshold || current != token.end_ ||
+ digit > max_last_digit) {
+ return decodeDouble(token, decoded);
}
- if ( currentBatch_->used_ == currentBatch_->end_ )
- {
- currentBatch_ = currentBatch_->next_;
- while ( currentBatch_ && currentBatch_->used_ == currentBatch_->end_ )
- currentBatch_ = currentBatch_->next_;
-
- if ( !currentBatch_ ) // no free batch found, allocate a new one
- {
- currentBatch_ = allocateBatch( objectsPerPage_ );
- currentBatch_->next_ = batches_; // insert at the head of the list
- batches_ = currentBatch_;
- }
+ }
+ value = value * 10 + digit;
+ }
+
+ if (isNegative) {
+ // We use the same magnitude assumption here, just in case.
+ const auto last_digit = static_cast<Value::UInt>(value % 10);
+ decoded = -Value::LargestInt(value / 10) * 10 - last_digit;
+ } else if (value <= Value::LargestUInt(Value::maxLargestInt)) {
+ decoded = Value::LargestInt(value);
+ } else {
+ decoded = value;
+ }
+
+ return true;
+}
+
+bool OurReader::decodeDouble(Token& token) {
+ Value decoded;
+ if (!decodeDouble(token, decoded))
+ return false;
+ currentValue().swapPayload(decoded);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ return true;
+}
+
+bool OurReader::decodeDouble(Token& token, Value& decoded) {
+ double value = 0;
+ const String buffer(token.start_, token.end_);
+ IStringStream is(buffer);
+ if (!(is >> value)) {
+ return addError(
+ "'" + String(token.start_, token.end_) + "' is not a number.", token);
+ }
+ decoded = value;
+ return true;
+}
+
+bool OurReader::decodeString(Token& token) {
+ String decoded_string;
+ if (!decodeString(token, decoded_string))
+ return false;
+ Value decoded(decoded_string);
+ currentValue().swapPayload(decoded);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ return true;
+}
+
+bool OurReader::decodeString(Token& token, String& decoded) {
+ decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
+ Location current = token.start_ + 1; // skip '"'
+ Location end = token.end_ - 1; // do not include '"'
+ while (current != end) {
+ Char c = *current++;
+ if (c == '"')
+ break;
+ if (c == '\\') {
+ if (current == end)
+ return addError("Empty escape sequence in string", token, current);
+ Char escape = *current++;
+ switch (escape) {
+ case '"':
+ decoded += '"';
+ break;
+ case '/':
+ decoded += '/';
+ break;
+ case '\\':
+ decoded += '\\';
+ break;
+ case 'b':
+ decoded += '\b';
+ break;
+ case 'f':
+ decoded += '\f';
+ break;
+ case 'n':
+ decoded += '\n';
+ break;
+ case 'r':
+ decoded += '\r';
+ break;
+ case 't':
+ decoded += '\t';
+ break;
+ case 'u': {
+ unsigned int unicode;
+ if (!decodeUnicodeCodePoint(token, current, end, unicode))
+ return false;
+ decoded += codePointToUTF8(unicode);
+ } break;
+ default:
+ return addError("Bad escape sequence in string", token, current);
}
- AllocatedType *allocated = currentBatch_->used_;
- currentBatch_->used_ += objectPerAllocation;
- return allocated;
- }
-
- /// Release the object.
- /// @warning it is the responsability of the caller to actually destruct the object.
- void release( AllocatedType *object )
- {
- assert( object != 0 );
- *static_cast<AllocatedType **>(object) = freeHead_;
- freeHead_ = object;
- }
+ } else {
+ decoded += c;
+ }
+ }
+ return true;
+}
+
+bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
+ Location end, unsigned int& unicode) {
+
+ if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
+ return false;
+ if (unicode >= 0xD800 && unicode <= 0xDBFF) {
+ // surrogate pairs
+ if (end - current < 6)
+ return addError(
+ "additional six characters expected to parse unicode surrogate pair.",
+ token, current);
+ if (*(current++) == '\\' && *(current++) == 'u') {
+ unsigned int surrogatePair;
+ if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
+ unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
+ } else
+ return false;
+ } else
+ return addError("expecting another \\u token to begin the second half of "
+ "a unicode surrogate pair",
+ token, current);
+ }
+ return true;
+}
+
+bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
+ Location end,
+ unsigned int& ret_unicode) {
+ if (end - current < 4)
+ return addError(
+ "Bad unicode escape sequence in string: four digits expected.", token,
+ current);
+ int unicode = 0;
+ for (int index = 0; index < 4; ++index) {
+ Char c = *current++;
+ unicode *= 16;
+ if (c >= '0' && c <= '9')
+ unicode += c - '0';
+ else if (c >= 'a' && c <= 'f')
+ unicode += c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ unicode += c - 'A' + 10;
+ else
+ return addError(
+ "Bad unicode escape sequence in string: hexadecimal digit expected.",
+ token, current);
+ }
+ ret_unicode = static_cast<unsigned int>(unicode);
+ return true;
+}
+
+bool OurReader::addError(const String& message, Token& token, Location extra) {
+ ErrorInfo info;
+ info.token_ = token;
+ info.message_ = message;
+ info.extra_ = extra;
+ errors_.push_back(info);
+ return false;
+}
+
+bool OurReader::recoverFromError(TokenType skipUntilToken) {
+ size_t errorCount = errors_.size();
+ Token skip;
+ for (;;) {
+ if (!readToken(skip))
+ errors_.resize(errorCount); // discard errors caused by recovery
+ if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
+ break;
+ }
+ errors_.resize(errorCount);
+ return false;
+}
+
+bool OurReader::addErrorAndRecover(const String& message, Token& token,
+ TokenType skipUntilToken) {
+ addError(message, token);
+ return recoverFromError(skipUntilToken);
+}
+
+Value& OurReader::currentValue() { return *(nodes_.top()); }
+
+OurReader::Char OurReader::getNextChar() {
+ if (current_ == end_)
+ return 0;
+ return *current_++;
+}
+
+void OurReader::getLocationLineAndColumn(Location location, int& line,
+ int& column) const {
+ Location current = begin_;
+ Location lastLineStart = current;
+ line = 0;
+ while (current < location && current != end_) {
+ Char c = *current++;
+ if (c == '\r') {
+ if (*current == '\n')
+ ++current;
+ lastLineStart = current;
+ ++line;
+ } else if (c == '\n') {
+ lastLineStart = current;
+ ++line;
+ }
+ }
+ // column & line start at 1
+ column = int(location - lastLineStart) + 1;
+ ++line;
+}
+
+String OurReader::getLocationLineAndColumn(Location location) const {
+ int line, column;
+ getLocationLineAndColumn(location, line, column);
+ char buffer[18 + 16 + 16 + 1];
+ jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
+ return buffer;
+}
+
+String OurReader::getFormattedErrorMessages() const {
+ String formattedMessage;
+ for (const auto& error : errors_) {
+ formattedMessage +=
+ "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
+ formattedMessage += " " + error.message_ + "\n";
+ if (error.extra_)
+ formattedMessage +=
+ "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
+ }
+ return formattedMessage;
+}
+
+std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
+ std::vector<OurReader::StructuredError> allErrors;
+ for (const auto& error : errors_) {
+ OurReader::StructuredError structured;
+ structured.offset_start = error.token_.start_ - begin_;
+ structured.offset_limit = error.token_.end_ - begin_;
+ structured.message = error.message_;
+ allErrors.push_back(structured);
+ }
+ return allErrors;
+}
+
+class OurCharReader : public CharReader {
+ bool const collectComments_;
+ OurReader reader_;
-private:
- struct BatchInfo
- {
- BatchInfo *next_;
- AllocatedType *used_;
- AllocatedType *end_;
- AllocatedType buffer_[objectPerAllocation];
- };
-
- // disabled copy constructor and assignement operator.
- BatchAllocator( const BatchAllocator & );
- void operator =( const BatchAllocator &);
-
- static BatchInfo *allocateBatch( unsigned int objectsPerPage )
- {
- const unsigned int mallocSize = sizeof(BatchInfo) - sizeof(AllocatedType)* objectPerAllocation
- + sizeof(AllocatedType) * objectPerAllocation * objectsPerPage;
- BatchInfo *batch = static_cast<BatchInfo*>( malloc( mallocSize ) );
- batch->next_ = 0;
- batch->used_ = batch->buffer_;
- batch->end_ = batch->buffer_ + objectsPerPage;
- return batch;
- }
-
- BatchInfo *batches_;
- BatchInfo *currentBatch_;
- /// Head of a single linked list within the allocated space of freeed object
- AllocatedType *freeHead_;
- unsigned int objectsPerPage_;
+public:
+ OurCharReader(bool collectComments, OurFeatures const& features)
+ : collectComments_(collectComments), reader_(features) {}
+ bool parse(char const* beginDoc, char const* endDoc, Value* root,
+ String* errs) override {
+ bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
+ if (errs) {
+ *errs = reader_.getFormattedErrorMessages();
+ }
+ return ok;
+ }
};
+CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
+CharReaderBuilder::~CharReaderBuilder() = default;
+CharReader* CharReaderBuilder::newCharReader() const {
+ bool collectComments = settings_["collectComments"].asBool();
+ OurFeatures features = OurFeatures::all();
+ features.allowComments_ = settings_["allowComments"].asBool();
+ features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool();
+ features.strictRoot_ = settings_["strictRoot"].asBool();
+ features.allowDroppedNullPlaceholders_ =
+ settings_["allowDroppedNullPlaceholders"].asBool();
+ features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
+ features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
+
+ // Stack limit is always a size_t, so we get this as an unsigned int
+ // regardless of it we have 64-bit integer support enabled.
+ features.stackLimit_ = static_cast<size_t>(settings_["stackLimit"].asUInt());
+ features.failIfExtra_ = settings_["failIfExtra"].asBool();
+ features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
+ features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
+ features.skipBom_ = settings_["skipBom"].asBool();
+ return new OurCharReader(collectComments, features);
+}
+
+bool CharReaderBuilder::validate(Json::Value* invalid) const {
+ static const auto& valid_keys = *new std::set<String>{
+ "collectComments",
+ "allowComments",
+ "allowTrailingCommas",
+ "strictRoot",
+ "allowDroppedNullPlaceholders",
+ "allowNumericKeys",
+ "allowSingleQuotes",
+ "stackLimit",
+ "failIfExtra",
+ "rejectDupKeys",
+ "allowSpecialFloats",
+ "skipBom",
+ };
+ for (auto si = settings_.begin(); si != settings_.end(); ++si) {
+ auto key = si.name();
+ if (valid_keys.count(key))
+ continue;
+ if (invalid)
+ (*invalid)[key] = *si;
+ else
+ return false;
+ }
+ return invalid ? invalid->empty() : true;
+}
+
+Value& CharReaderBuilder::operator[](const String& key) {
+ return settings_[key];
+}
+// static
+void CharReaderBuilder::strictMode(Json::Value* settings) {
+ //! [CharReaderBuilderStrictMode]
+ (*settings)["allowComments"] = false;
+ (*settings)["allowTrailingCommas"] = false;
+ (*settings)["strictRoot"] = true;
+ (*settings)["allowDroppedNullPlaceholders"] = false;
+ (*settings)["allowNumericKeys"] = false;
+ (*settings)["allowSingleQuotes"] = false;
+ (*settings)["stackLimit"] = 1000;
+ (*settings)["failIfExtra"] = true;
+ (*settings)["rejectDupKeys"] = true;
+ (*settings)["allowSpecialFloats"] = false;
+ (*settings)["skipBom"] = true;
+ //! [CharReaderBuilderStrictMode]
+}
+// static
+void CharReaderBuilder::setDefaults(Json::Value* settings) {
+ //! [CharReaderBuilderDefaults]
+ (*settings)["collectComments"] = true;
+ (*settings)["allowComments"] = true;
+ (*settings)["allowTrailingCommas"] = true;
+ (*settings)["strictRoot"] = false;
+ (*settings)["allowDroppedNullPlaceholders"] = false;
+ (*settings)["allowNumericKeys"] = false;
+ (*settings)["allowSingleQuotes"] = false;
+ (*settings)["stackLimit"] = 1000;
+ (*settings)["failIfExtra"] = false;
+ (*settings)["rejectDupKeys"] = false;
+ (*settings)["allowSpecialFloats"] = false;
+ (*settings)["skipBom"] = true;
+ //! [CharReaderBuilderDefaults]
+}
+
+//////////////////////////////////
+// global functions
+
+bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,
+ String* errs) {
+ OStringStream ssin;
+ ssin << sin.rdbuf();
+ String doc = ssin.str();
+ char const* begin = doc.data();
+ char const* end = begin + doc.size();
+ // Note that we do not actually need a null-terminator.
+ CharReaderPtr const reader(fact.newCharReader());
+ return reader->parse(begin, end, root, errs);
+}
+
+IStream& operator>>(IStream& sin, Value& root) {
+ CharReaderBuilder b;
+ String errs;
+ bool ok = parseFromStream(b, sin, &root, &errs);
+ if (!ok) {
+ throwRuntimeError(errs);
+ }
+ return sin;
+}
} // namespace Json
-# endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION
-
-#endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED
-
-
// //////////////////////////////////////////////////////////////////////
-// End of content of file: src/lib_json/json_batchallocator.h
+// End of content of file: src/lib_json/json_reader.cpp
// //////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: src/lib_json/json_valueiterator.inl
// //////////////////////////////////////////////////////////////////////
-// Copyright 2007-2010 Baptiste Lepilleur
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
// included by json_value.cpp
namespace Json {
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueIteratorBase
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
-ValueIteratorBase::ValueIteratorBase()
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- : current_()
- , isNull_( true )
-{
-}
-#else
- : isArray_( true )
- , isNull_( true )
-{
- iterator_.array_ = ValueInternalArray::IteratorState();
-}
-#endif
+ValueIteratorBase::ValueIteratorBase() : current_() {}
+ValueIteratorBase::ValueIteratorBase(
+ const Value::ObjectValues::iterator& current)
+ : current_(current), isNull_(false) {}
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator &current )
- : current_( current )
- , isNull_( false )
-{
-}
-#else
-ValueIteratorBase::ValueIteratorBase( const ValueInternalArray::IteratorState &state )
- : isArray_( true )
-{
- iterator_.array_ = state;
-}
+Value& ValueIteratorBase::deref() { return current_->second; }
+const Value& ValueIteratorBase::deref() const { return current_->second; }
+void ValueIteratorBase::increment() { ++current_; }
-ValueIteratorBase::ValueIteratorBase( const ValueInternalMap::IteratorState &state )
- : isArray_( false )
-{
- iterator_.map_ = state;
-}
-#endif
-
-Value &
-ValueIteratorBase::deref() const
-{
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- return current_->second;
-#else
- if ( isArray_ )
- return ValueInternalArray::dereference( iterator_.array_ );
- return ValueInternalMap::value( iterator_.map_ );
-#endif
-}
-
-
-void
-ValueIteratorBase::increment()
-{
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- ++current_;
-#else
- if ( isArray_ )
- ValueInternalArray::increment( iterator_.array_ );
- ValueInternalMap::increment( iterator_.map_ );
-#endif
-}
-
-
-void
-ValueIteratorBase::decrement()
-{
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- --current_;
-#else
- if ( isArray_ )
- ValueInternalArray::decrement( iterator_.array_ );
- ValueInternalMap::decrement( iterator_.map_ );
-#endif
-}
-
+void ValueIteratorBase::decrement() { --current_; }
ValueIteratorBase::difference_type
-ValueIteratorBase::computeDistance( const SelfType &other ) const
-{
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-# ifdef JSON_USE_CPPTL_SMALLMAP
- return current_ - other.current_;
-# else
- // Iterator for null value are initialized using the default
- // constructor, which initialize current_ to the default
- // std::map::iterator. As begin() and end() are two instance
- // of the default std::map::iterator, they can not be compared.
- // To allow this, we handle this comparison specifically.
- if ( isNull_ && other.isNull_ )
- {
- return 0;
- }
-
-
- // Usage of std::distance is not portable (does not compile with Sun Studio 12 RogueWave STL,
- // which is the one used by default).
- // Using a portable hand-made version for non random iterator instead:
- // return difference_type( std::distance( current_, other.current_ ) );
- difference_type myDistance = 0;
- for ( Value::ObjectValues::iterator it = current_; it != other.current_; ++it )
- {
- ++myDistance;
- }
- return myDistance;
-# endif
-#else
- if ( isArray_ )
- return ValueInternalArray::distance( iterator_.array_, other.iterator_.array_ );
- return ValueInternalMap::distance( iterator_.map_, other.iterator_.map_ );
-#endif
-}
-
-
-bool
-ValueIteratorBase::isEqual( const SelfType &other ) const
-{
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- if ( isNull_ )
- {
- return other.isNull_;
- }
- return current_ == other.current_;
-#else
- if ( isArray_ )
- return ValueInternalArray::equals( iterator_.array_, other.iterator_.array_ );
- return ValueInternalMap::equals( iterator_.map_, other.iterator_.map_ );
-#endif
-}
-
-
-void
-ValueIteratorBase::copy( const SelfType &other )
-{
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- current_ = other.current_;
-#else
- if ( isArray_ )
- iterator_.array_ = other.iterator_.array_;
- iterator_.map_ = other.iterator_.map_;
-#endif
-}
-
-
-Value
-ValueIteratorBase::key() const
-{
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- const Value::CZString czstring = (*current_).first;
- if ( czstring.c_str() )
- {
- if ( czstring.isStaticString() )
- return Value( StaticString( czstring.c_str() ) );
- return Value( czstring.c_str() );
- }
- return Value( czstring.index() );
-#else
- if ( isArray_ )
- return Value( ValueInternalArray::indexOf( iterator_.array_ ) );
- bool isStatic;
- const char *memberName = ValueInternalMap::key( iterator_.map_, isStatic );
- if ( isStatic )
- return Value( StaticString( memberName ) );
- return Value( memberName );
-#endif
-}
-
-
-UInt
-ValueIteratorBase::index() const
-{
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- const Value::CZString czstring = (*current_).first;
- if ( !czstring.c_str() )
- return czstring.index();
- return Value::UInt( -1 );
-#else
- if ( isArray_ )
- return Value::UInt( ValueInternalArray::indexOf( iterator_.array_ ) );
- return Value::UInt( -1 );
-#endif
-}
-
-
-const char *
-ValueIteratorBase::memberName() const
-{
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- const char *name = (*current_).first.c_str();
- return name ? name : "";
-#else
- if ( !isArray_ )
- return ValueInternalMap::key( iterator_.map_ );
- return "";
-#endif
+ValueIteratorBase::computeDistance(const SelfType& other) const {
+ // Iterator for null value are initialized using the default
+ // constructor, which initialize current_ to the default
+ // std::map::iterator. As begin() and end() are two instance
+ // of the default std::map::iterator, they can not be compared.
+ // To allow this, we handle this comparison specifically.
+ if (isNull_ && other.isNull_) {
+ return 0;
+ }
+
+ // Usage of std::distance is not portable (does not compile with Sun Studio 12
+ // RogueWave STL,
+ // which is the one used by default).
+ // Using a portable hand-made version for non random iterator instead:
+ // return difference_type( std::distance( current_, other.current_ ) );
+ difference_type myDistance = 0;
+ for (Value::ObjectValues::iterator it = current_; it != other.current_;
+ ++it) {
+ ++myDistance;
+ }
+ return myDistance;
+}
+
+bool ValueIteratorBase::isEqual(const SelfType& other) const {
+ if (isNull_) {
+ return other.isNull_;
+ }
+ return current_ == other.current_;
+}
+
+void ValueIteratorBase::copy(const SelfType& other) {
+ current_ = other.current_;
+ isNull_ = other.isNull_;
+}
+
+Value ValueIteratorBase::key() const {
+ const Value::CZString czstring = (*current_).first;
+ if (czstring.data()) {
+ if (czstring.isStaticString())
+ return Value(StaticString(czstring.data()));
+ return Value(czstring.data(), czstring.data() + czstring.length());
+ }
+ return Value(czstring.index());
+}
+
+UInt ValueIteratorBase::index() const {
+ const Value::CZString czstring = (*current_).first;
+ if (!czstring.data())
+ return czstring.index();
+ return Value::UInt(-1);
+}
+
+String ValueIteratorBase::name() const {
+ char const* keey;
+ char const* end;
+ keey = memberName(&end);
+ if (!keey)
+ return String();
+ return String(keey, end);
+}
+
+char const* ValueIteratorBase::memberName() const {
+ const char* cname = (*current_).first.data();
+ return cname ? cname : "";
+}
+
+char const* ValueIteratorBase::memberName(char const** end) const {
+ const char* cname = (*current_).first.data();
+ if (!cname) {
+ *end = nullptr;
+ return nullptr;
+ }
+ *end = cname + (*current_).first.length();
+ return cname;
}
-
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueConstIterator
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
-ValueConstIterator::ValueConstIterator()
-{
-}
+ValueConstIterator::ValueConstIterator() = default;
+ValueConstIterator::ValueConstIterator(
+ const Value::ObjectValues::iterator& current)
+ : ValueIteratorBase(current) {}
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator &current )
- : ValueIteratorBase( current )
-{
-}
-#else
-ValueConstIterator::ValueConstIterator( const ValueInternalArray::IteratorState &state )
- : ValueIteratorBase( state )
-{
-}
-
-ValueConstIterator::ValueConstIterator( const ValueInternalMap::IteratorState &state )
- : ValueIteratorBase( state )
-{
-}
-#endif
+ValueConstIterator::ValueConstIterator(ValueIterator const& other)
+ : ValueIteratorBase(other) {}
-ValueConstIterator &
-ValueConstIterator::operator =( const ValueIteratorBase &other )
-{
- copy( other );
- return *this;
+ValueConstIterator& ValueConstIterator::
+operator=(const ValueIteratorBase& other) {
+ copy(other);
+ return *this;
}
-
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueIterator
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
-ValueIterator::ValueIterator()
-{
-}
+ValueIterator::ValueIterator() = default;
+ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current)
+ : ValueIteratorBase(current) {}
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-ValueIterator::ValueIterator( const Value::ObjectValues::iterator &current )
- : ValueIteratorBase( current )
-{
-}
-#else
-ValueIterator::ValueIterator( const ValueInternalArray::IteratorState &state )
- : ValueIteratorBase( state )
-{
+ValueIterator::ValueIterator(const ValueConstIterator& other)
+ : ValueIteratorBase(other) {
+ throwRuntimeError("ConstIterator to Iterator should never be allowed.");
}
-ValueIterator::ValueIterator( const ValueInternalMap::IteratorState &state )
- : ValueIteratorBase( state )
-{
-}
-#endif
-
-ValueIterator::ValueIterator( const ValueConstIterator &other )
- : ValueIteratorBase( other )
-{
-}
+ValueIterator::ValueIterator(const ValueIterator& other) = default;
-ValueIterator::ValueIterator( const ValueIterator &other )
- : ValueIteratorBase( other )
-{
-}
-
-ValueIterator &
-ValueIterator::operator =( const SelfType &other )
-{
- copy( other );
- return *this;
+ValueIterator& ValueIterator::operator=(const SelfType& other) {
+ copy(other);
+ return *this;
}
} // namespace Json
// //////////////////////////////////////////////////////////////////////
// End of content of file: src/lib_json/json_valueiterator.inl
// //////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: src/lib_json/json_value.cpp
// //////////////////////////////////////////////////////////////////////
-// Copyright 2007-2010 Baptiste Lepilleur
+// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#if !defined(JSON_IS_AMALGAMATION)
-# include <json/value.h>
-# include <json/writer.h>
-# ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
-# include "json_batchallocator.h"
-# endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
+#include <json/assertions.h>
+#include <json/value.h>
+#include <json/writer.h>
#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstddef>
+#include <cstring>
#include <iostream>
+#include <sstream>
#include <utility>
-#include <stdexcept>
-#include <cstring>
-#include <cassert>
-#ifdef JSON_USE_CPPTL
-# include <cpptl/conststring.h>
+
+// Provide implementation equivalent of std::snprintf for older _MSC compilers
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#include <stdarg.h>
+static int msvc_pre1900_c99_vsnprintf(char* outBuf, size_t size,
+ const char* format, va_list ap) {
+ int count = -1;
+ if (size != 0)
+ count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
+ if (count == -1)
+ count = _vscprintf(format, ap);
+ return count;
+}
+
+int JSON_API msvc_pre1900_c99_snprintf(char* outBuf, size_t size,
+ const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ const int count = msvc_pre1900_c99_vsnprintf(outBuf, size, format, ap);
+ va_end(ap);
+ return count;
+}
+#endif
+
+// Disable warning C4702 : unreachable code
+#if defined(_MSC_VER)
+#pragma warning(disable : 4702)
#endif
-#include <cstddef> // size_t
-#define JSON_ASSERT_UNREACHABLE assert( false )
-#define JSON_ASSERT( condition ) assert( condition ); // @todo <= change this into an exception throw
-#define JSON_FAIL_MESSAGE( message ) throw std::runtime_error( message );
-#define JSON_ASSERT_MESSAGE( condition, message ) if (!( condition )) JSON_FAIL_MESSAGE( message )
+#define JSON_ASSERT_UNREACHABLE assert(false)
namespace Json {
+template <typename T>
+static std::unique_ptr<T> cloneUnique(const std::unique_ptr<T>& p) {
+ std::unique_ptr<T> r;
+ if (p) {
+ r = std::unique_ptr<T>(new T(*p));
+ }
+ return r;
+}
+
+// This is a walkaround to avoid the static initialization of Value::null.
+// kNull must be word-aligned to avoid crashing on ARM. We use an alignment of
+// 8 (instead of 4) as a bit of future-proofing.
+#if defined(__ARMEL__)
+#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
+#else
+#define ALIGNAS(byte_alignment)
+#endif
+
+// static
+Value const& Value::nullSingleton() {
+ static Value const nullStatic;
+ return nullStatic;
+}
-const Value Value::null;
-const Int Value::minInt = Int( ~(UInt(-1)/2) );
-const Int Value::maxInt = Int( UInt(-1)/2 );
-const UInt Value::maxUInt = UInt(-1);
-const Int64 Value::minInt64 = Int64( ~(UInt64(-1)/2) );
-const Int64 Value::maxInt64 = Int64( UInt64(-1)/2 );
-const UInt64 Value::maxUInt64 = UInt64(-1);
-const LargestInt Value::minLargestInt = LargestInt( ~(LargestUInt(-1)/2) );
-const LargestInt Value::maxLargestInt = LargestInt( LargestUInt(-1)/2 );
-const LargestUInt Value::maxLargestUInt = LargestUInt(-1);
+#if JSON_USE_NULLREF
+// for backwards compatibility, we'll leave these global references around, but
+// DO NOT use them in JSONCPP library code any more!
+// static
+Value const& Value::null = Value::nullSingleton();
+// static
+Value const& Value::nullRef = Value::nullSingleton();
+#endif
+
+#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+template <typename T, typename U>
+static inline bool InRange(double d, T min, U max) {
+ // The casts can lose precision, but we are looking only for
+ // an approximate range. Might fail on edge cases though. ~cdunn
+ return d >= static_cast<double>(min) && d <= static_cast<double>(max);
+}
+#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+static inline double integerToDouble(Json::UInt64 value) {
+ return static_cast<double>(Int64(value / 2)) * 2.0 +
+ static_cast<double>(Int64(value & 1));
+}
-/// Unknown size marker
-static const unsigned int unknown = -1;
+template <typename T> static inline double integerToDouble(T value) {
+ return static_cast<double>(value);
+}
+template <typename T, typename U>
+static inline bool InRange(double d, T min, U max) {
+ return d >= integerToDouble(min) && d <= integerToDouble(max);
+}
+#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
/** Duplicates the specified string value.
* @param value Pointer to the string to duplicate. Must be zero-terminated if
* length is "unknown".
* @param length Length of the value. if equals to unknown, then it will be
* computed using strlen(value).
* @return Pointer on the duplicate instance of string.
*/
-static inline char *
-duplicateStringValue( const char *value,
- unsigned int length = unknown )
-{
- if ( length == unknown )
- length = static_cast<unsigned int>(strlen(value));
- char *newString = static_cast<char *>( malloc( length + 1 ) );
- JSON_ASSERT_MESSAGE( newString != nullptr, "Failed to allocate string value buffer" );
- memcpy( newString, value, length );
- newString[length] = 0;
- return newString;
-}
-
-
-/** Free the string duplicated by duplicateStringValue().
+static inline char* duplicateStringValue(const char* value, size_t length) {
+ // Avoid an integer overflow in the call to malloc below by limiting length
+ // to a sane value.
+ if (length >= static_cast<size_t>(Value::maxInt))
+ length = Value::maxInt - 1;
+
+ auto newString = static_cast<char*>(malloc(length + 1));
+ if (newString == nullptr) {
+ throwRuntimeError("in Json::Value::duplicateStringValue(): "
+ "Failed to allocate string value buffer");
+ }
+ memcpy(newString, value, length);
+ newString[length] = 0;
+ return newString;
+}
+
+/* Record the length as a prefix.
*/
-static inline void
-releaseStringValue( char *value )
-{
- if ( value )
- free( value );
-}
+static inline char* duplicateAndPrefixStringValue(const char* value,
+ unsigned int length) {
+ // Avoid an integer overflow in the call to malloc below by limiting length
+ // to a sane value.
+ JSON_ASSERT_MESSAGE(length <= static_cast<unsigned>(Value::maxInt) -
+ sizeof(unsigned) - 1U,
+ "in Json::Value::duplicateAndPrefixStringValue(): "
+ "length too big for prefixing");
+ size_t actualLength = sizeof(length) + length + 1;
+ auto newString = static_cast<char*>(malloc(actualLength));
+ if (newString == nullptr) {
+ throwRuntimeError("in Json::Value::duplicateAndPrefixStringValue(): "
+ "Failed to allocate string value buffer");
+ }
+ *reinterpret_cast<unsigned*>(newString) = length;
+ memcpy(newString + sizeof(unsigned), value, length);
+ newString[actualLength - 1U] =
+ 0; // to avoid buffer over-run accidents by users later
+ return newString;
+}
+inline static void decodePrefixedString(bool isPrefixed, char const* prefixed,
+ unsigned* length, char const** value) {
+ if (!isPrefixed) {
+ *length = static_cast<unsigned>(strlen(prefixed));
+ *value = prefixed;
+ } else {
+ *length = *reinterpret_cast<unsigned const*>(prefixed);
+ *value = prefixed + sizeof(unsigned);
+ }
+}
+/** Free the string duplicated by
+ * duplicateStringValue()/duplicateAndPrefixStringValue().
+ */
+#if JSONCPP_USING_SECURE_MEMORY
+static inline void releasePrefixedStringValue(char* value) {
+ unsigned length = 0;
+ char const* valueDecoded;
+ decodePrefixedString(true, value, &length, &valueDecoded);
+ size_t const size = sizeof(unsigned) + length + 1U;
+ memset(value, 0, size);
+ free(value);
+}
+static inline void releaseStringValue(char* value, unsigned length) {
+ // length==0 => we allocated the strings memory
+ size_t size = (length == 0) ? strlen(value) : length;
+ memset(value, 0, size);
+ free(value);
+}
+#else // !JSONCPP_USING_SECURE_MEMORY
+static inline void releasePrefixedStringValue(char* value) { free(value); }
+static inline void releaseStringValue(char* value, unsigned) { free(value); }
+#endif // JSONCPP_USING_SECURE_MEMORY
} // namespace Json
-
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// ValueInternals...
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
#if !defined(JSON_IS_AMALGAMATION)
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
-# include "json_internalarray.inl"
-# include "json_internalmap.inl"
-# endif // JSON_VALUE_USE_INTERNAL_MAP
-# include "json_valueiterator.inl"
+#include "json_valueiterator.inl"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// class Value::CommentInfo
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-
-
-Value::CommentInfo::CommentInfo()
- : comment_( nullptr )
-{
+#if JSON_USE_EXCEPTION
+Exception::Exception(String msg) : msg_(std::move(msg)) {}
+Exception::~Exception() noexcept = default;
+char const* Exception::what() const noexcept { return msg_.c_str(); }
+RuntimeError::RuntimeError(String const& msg) : Exception(msg) {}
+LogicError::LogicError(String const& msg) : Exception(msg) {}
+JSONCPP_NORETURN void throwRuntimeError(String const& msg) {
+ throw RuntimeError(msg);
}
-
-Value::CommentInfo::~CommentInfo()
-{
- if ( comment_ )
- releaseStringValue( comment_ );
+JSONCPP_NORETURN void throwLogicError(String const& msg) {
+ throw LogicError(msg);
}
-
-
-void
-Value::CommentInfo::setComment( const char *text )
-{
- if ( comment_ )
- releaseStringValue( comment_ );
- JSON_ASSERT( text != nullptr );
- JSON_ASSERT_MESSAGE( text[0]=='\0' || text[0]=='/', "Comments must start with /");
- // It seems that /**/ style comments are acceptable as well.
- comment_ = duplicateStringValue( text );
+#else // !JSON_USE_EXCEPTION
+JSONCPP_NORETURN void throwRuntimeError(String const& msg) {
+ std::cerr << msg << std::endl;
+ abort();
}
-
-
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// class Value::CZString
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-# ifndef JSON_VALUE_USE_INTERNAL_MAP
-
-// Notes: index_ indicates if the string was allocated when
-// a string is stored.
-
-Value::CZString::CZString( ArrayIndex index )
- : cstr_( nullptr )
- , index_( index )
-{
-}
-
-Value::CZString::CZString( const char *cstr, DuplicationPolicy allocate )
- : cstr_( allocate == duplicate ? duplicateStringValue(cstr)
- : cstr )
- , index_( allocate )
-{
-}
-
-Value::CZString::CZString( const CZString &other )
-: cstr_( other.index_ != noDuplication && other.cstr_ != nullptr
- ? duplicateStringValue( other.cstr_ )
- : other.cstr_ )
- , index_( other.cstr_ ? (other.index_ == noDuplication ? static_cast<ArrayIndex>(noDuplication) : static_cast<ArrayIndex>(duplicate))
- : other.index_ )
-{
-}
-
-Value::CZString::~CZString()
-{
- if ( cstr_ && index_ == duplicate )
- releaseStringValue( const_cast<char *>( cstr_ ) );
-}
-
-void
-Value::CZString::swap( CZString &other )
-{
- std::swap( cstr_, other.cstr_ );
- std::swap( index_, other.index_ );
-}
-
-Value::CZString &
-Value::CZString::operator =( const CZString &other )
-{
- CZString temp( other );
- swap( temp );
- return *this;
-}
-
-bool
-Value::CZString::operator<( const CZString &other ) const
-{
- if ( cstr_ )
- return strcmp( cstr_, other.cstr_ ) < 0;
- return index_ < other.index_;
-}
-
-bool
-Value::CZString::operator==( const CZString &other ) const
-{
- if ( cstr_ )
- return strcmp( cstr_, other.cstr_ ) == 0;
- return index_ == other.index_;
+JSONCPP_NORETURN void throwLogicError(String const& msg) {
+ std::cerr << msg << std::endl;
+ abort();
}
-
-
-ArrayIndex
-Value::CZString::index() const
-{
- return index_;
-}
-
-
-const char *
-Value::CZString::c_str() const
-{
- return cstr_;
-}
-
-bool
-Value::CZString::isStaticString() const
-{
- return index_ == noDuplication;
-}
-
-#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP
-
+#endif
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
-// class Value::Value
+// class Value::CZString
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
-/*! \internal Default constructor initialization must be equivalent to:
- * memset( this, 0, sizeof(Value) )
- * This optimization is used in ValueInternalMap fast allocator.
- */
-Value::Value( ValueType type )
- : type_( type )
- , allocated_( 0 )
- , comments_( nullptr )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
- , itemIsUsed_( 0 )
-#endif
-{
- switch ( type )
- {
- case nullValue:
- break;
- case intValue:
- case uintValue:
- value_.int_ = 0;
- break;
- case realValue:
- value_.real_ = 0.0;
- break;
- case stringValue:
- value_.string_ = nullptr;
- break;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- case arrayValue:
- case objectValue:
- value_.map_ = new ObjectValues();
- break;
-#else
- case arrayValue:
- value_.array_ = arrayAllocator()->newArray();
- break;
- case objectValue:
- value_.map_ = mapAllocator()->newMap();
- break;
-#endif
- case booleanValue:
- value_.bool_ = false;
- break;
- default:
- JSON_ASSERT_UNREACHABLE;
- }
-}
-
-
-#if defined(JSON_HAS_INT64)
-Value::Value( UInt value )
- : type_( uintValue )
- , comments_( nullptr )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
- , itemIsUsed_( 0 )
-#endif
-{
- value_.uint_ = value;
-}
-
-Value::Value( Int value )
- : type_( intValue )
- , comments_( nullptr )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
- , itemIsUsed_( 0 )
-#endif
-{
- value_.int_ = value;
-}
-
-#endif // if defined(JSON_HAS_INT64)
-
-
-Value::Value( Int64 value )
- : type_( intValue )
- , comments_( nullptr )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
- , itemIsUsed_( 0 )
-#endif
-{
- value_.int_ = value;
-}
-
-
-Value::Value( UInt64 value )
- : type_( uintValue )
- , comments_( nullptr )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
- , itemIsUsed_( 0 )
-#endif
-{
- value_.uint_ = value;
-}
-
-Value::Value( double value )
- : type_( realValue )
- , comments_( nullptr )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
- , itemIsUsed_( 0 )
-#endif
-{
- value_.real_ = value;
-}
-
-Value::Value( const char *value )
- : type_( stringValue )
- , allocated_( true )
- , comments_( nullptr )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
- , itemIsUsed_( 0 )
-#endif
-{
- value_.string_ = duplicateStringValue( value );
-}
-
-
-Value::Value( const char *beginValue,
- const char *endValue )
- : type_( stringValue )
- , allocated_( true )
- , comments_( nullptr )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
- , itemIsUsed_( 0 )
-#endif
-{
- value_.string_ = duplicateStringValue( beginValue,
- static_cast<unsigned int>(endValue - beginValue) );
-}
-
-
-Value::Value( const std::string &value )
- : type_( stringValue )
- , allocated_( true )
- , comments_( nullptr )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
- , itemIsUsed_( 0 )
-#endif
-{
- value_.string_ = duplicateStringValue( value.c_str(),
- static_cast<unsigned int>(value.length()) );
-
-}
-
-Value::Value( const StaticString &value )
- : type_( stringValue )
- , allocated_( false )
- , comments_( nullptr )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
- , itemIsUsed_( 0 )
-#endif
-{
- value_.string_ = const_cast<char *>( value.c_str() );
-}
-
-
-# ifdef JSON_USE_CPPTL
-Value::Value( const CppTL::ConstString &value )
- : type_( stringValue )
- , allocated_( true )
- , comments_( 0 )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
- , itemIsUsed_( 0 )
-#endif
-{
- value_.string_ = duplicateStringValue( value, value.length() );
-}
-# endif
-
-Value::Value( bool value )
- : type_( booleanValue )
- , comments_( nullptr )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
- , itemIsUsed_( 0 )
-#endif
-{
- value_.bool_ = value;
-}
-
-
-Value::Value( const Value &other )
- : type_( other.type_ )
- , comments_( nullptr )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
- , itemIsUsed_( 0 )
-#endif
-{
- switch ( type_ )
- {
- case nullValue:
- case intValue:
- case uintValue:
- case realValue:
- case booleanValue:
- value_ = other.value_;
- break;
- case stringValue:
- if ( other.value_.string_ )
- {
- value_.string_ = duplicateStringValue( other.value_.string_ );
- allocated_ = true;
- }
- else
- value_.string_ = nullptr;
- break;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- case arrayValue:
- case objectValue:
- value_.map_ = new ObjectValues( *other.value_.map_ );
- break;
-#else
- case arrayValue:
- value_.array_ = arrayAllocator()->newArrayCopy( *other.value_.array_ );
- break;
- case objectValue:
- value_.map_ = mapAllocator()->newMapCopy( *other.value_.map_ );
- break;
-#endif
- default:
- JSON_ASSERT_UNREACHABLE;
- }
- if ( other.comments_ )
- {
- comments_ = new CommentInfo[numberOfCommentPlacement];
- for ( int comment =0; comment < numberOfCommentPlacement; ++comment )
- {
- const CommentInfo &otherComment = other.comments_[comment];
- if ( otherComment.comment_ )
- comments_[comment].setComment( otherComment.comment_ );
- }
- }
-}
-
-
-Value::~Value()
-{
- switch ( type_ )
- {
- case nullValue:
- case intValue:
- case uintValue:
- case realValue:
- case booleanValue:
- break;
- case stringValue:
- if ( allocated_ )
- releaseStringValue( value_.string_ );
- break;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- case arrayValue:
- case objectValue:
- delete value_.map_;
- break;
-#else
- case arrayValue:
- arrayAllocator()->destructArray( value_.array_ );
- break;
- case objectValue:
- mapAllocator()->destructMap( value_.map_ );
- break;
-#endif
- default:
- JSON_ASSERT_UNREACHABLE;
- }
-
- if ( comments_ )
- delete[] comments_;
-}
-
-Value &
-Value::operator=( const Value &other )
-{
- Value temp( other );
- swap( temp );
- return *this;
-}
-
-void
-Value::swap( Value &other )
-{
- ValueType temp = type_;
- type_ = other.type_;
- other.type_ = temp;
- std::swap( value_, other.value_ );
- int temp2 = allocated_;
- allocated_ = other.allocated_;
- other.allocated_ = temp2;
-}
-
-ValueType
-Value::type() const
-{
- return type_;
-}
-
-
-int
-Value::compare( const Value &other ) const
-{
- if ( *this < other )
- return -1;
- if ( *this > other )
- return 1;
- return 0;
-}
-
-
-bool
-Value::operator <( const Value &other ) const
-{
- int typeDelta = type_ - other.type_;
- if ( typeDelta )
- return typeDelta < 0 ? true : false;
- switch ( type_ )
- {
- case nullValue:
- return false;
- case intValue:
- return value_.int_ < other.value_.int_;
- case uintValue:
- return value_.uint_ < other.value_.uint_;
- case realValue:
- return value_.real_ < other.value_.real_;
- case booleanValue:
- return value_.bool_ < other.value_.bool_;
- case stringValue:
- return ( value_.string_ == nullptr && other.value_.string_ )
- || ( other.value_.string_
- && value_.string_
- && strcmp( value_.string_, other.value_.string_ ) < 0 );
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- case arrayValue:
- case objectValue:
- {
- int delta = int( value_.map_->size() - other.value_.map_->size() );
- if ( delta )
- return delta < 0;
- return (*value_.map_) < (*other.value_.map_);
- }
-#else
- case arrayValue:
- return value_.array_->compare( *(other.value_.array_) ) < 0;
- case objectValue:
- return value_.map_->compare( *(other.value_.map_) ) < 0;
-#endif
- default:
- JSON_ASSERT_UNREACHABLE;
- }
- return false; // unreachable
-}
-
-bool
-Value::operator <=( const Value &other ) const
-{
- return !(other < *this);
-}
-
-bool
-Value::operator >=( const Value &other ) const
-{
- return !(*this < other);
-}
-
-bool
-Value::operator >( const Value &other ) const
-{
- return other < *this;
-}
-
-bool
-Value::operator ==( const Value &other ) const
-{
- //if ( type_ != other.type_ )
- // GCC 2.95.3 says:
- // attempt to take address of bit-field structure member `Json::Value::type_'
- // Beats me, but a temp solves the problem.
- int temp = other.type_;
- if ( type_ != temp )
- return false;
- switch ( type_ )
- {
- case nullValue:
- return true;
- case intValue:
- return value_.int_ == other.value_.int_;
- case uintValue:
- return value_.uint_ == other.value_.uint_;
- case realValue:
- return value_.real_ == other.value_.real_;
- case booleanValue:
- return value_.bool_ == other.value_.bool_;
- case stringValue:
- return ( value_.string_ == other.value_.string_ )
- || ( other.value_.string_
- && value_.string_
- && strcmp( value_.string_, other.value_.string_ ) == 0 );
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- case arrayValue:
- case objectValue:
- return value_.map_->size() == other.value_.map_->size()
- && (*value_.map_) == (*other.value_.map_);
-#else
- case arrayValue:
- return value_.array_->compare( *(other.value_.array_) ) == 0;
- case objectValue:
- return value_.map_->compare( *(other.value_.map_) ) == 0;
-#endif
- default:
- JSON_ASSERT_UNREACHABLE;
- }
- return false; // unreachable
-}
-
-bool
-Value::operator !=( const Value &other ) const
-{
- return !( *this == other );
-}
-
-const char *
-Value::asCString() const
-{
- JSON_ASSERT( type_ == stringValue );
- return value_.string_;
-}
-
-
-std::string
-Value::asString() const
-{
- switch ( type_ )
- {
- case nullValue:
- return "";
- case stringValue:
- return value_.string_ ? value_.string_ : "";
- case booleanValue:
- return value_.bool_ ? "true" : "false";
- case intValue:
- case uintValue:
- case realValue:
- case arrayValue:
- case objectValue:
- JSON_FAIL_MESSAGE( "Type is not convertible to string" );
- default:
- JSON_ASSERT_UNREACHABLE;
- }
- return ""; // unreachable
-}
-
-# ifdef JSON_USE_CPPTL
-CppTL::ConstString
-Value::asConstString() const
-{
- return CppTL::ConstString( asString().c_str() );
-}
-# endif
-
-
-Value::Int
-Value::asInt() const
-{
- switch ( type_ )
- {
- case nullValue:
- return 0;
- case intValue:
- JSON_ASSERT_MESSAGE( value_.int_ >= minInt && value_.int_ <= maxInt, "unsigned integer out of signed int range" );
- return Int(value_.int_);
- case uintValue:
- JSON_ASSERT_MESSAGE( value_.uint_ <= UInt(maxInt), "unsigned integer out of signed int range" );
- return Int(value_.uint_);
- case realValue:
- JSON_ASSERT_MESSAGE( value_.real_ >= minInt && value_.real_ <= maxInt, "Real out of signed integer range" );
- return Int( value_.real_ );
- case booleanValue:
- return value_.bool_ ? 1 : 0;
- case stringValue:
- case arrayValue:
- case objectValue:
- JSON_FAIL_MESSAGE( "Type is not convertible to int" );
- default:
- JSON_ASSERT_UNREACHABLE;
- }
- return 0; // unreachable;
-}
-
-
-Value::UInt
-Value::asUInt() const
-{
- switch ( type_ )
- {
- case nullValue:
- return 0;
- case intValue:
- JSON_ASSERT_MESSAGE( value_.int_ >= 0, "Negative integer can not be converted to unsigned integer" );
- JSON_ASSERT_MESSAGE( value_.int_ <= maxUInt, "signed integer out of UInt range" );
- return UInt(value_.int_);
- case uintValue:
- JSON_ASSERT_MESSAGE( value_.uint_ <= maxUInt, "unsigned integer out of UInt range" );
- return UInt(value_.uint_);
- case realValue:
- JSON_ASSERT_MESSAGE( value_.real_ >= 0 && value_.real_ <= maxUInt, "Real out of unsigned integer range" );
- return UInt( value_.real_ );
- case booleanValue:
- return value_.bool_ ? 1 : 0;
- case stringValue:
- case arrayValue:
- case objectValue:
- JSON_FAIL_MESSAGE( "Type is not convertible to uint" );
- default:
- JSON_ASSERT_UNREACHABLE;
- }
- return 0; // unreachable;
-}
-
-
-# if defined(JSON_HAS_INT64)
-
-Value::Int64
-Value::asInt64() const
-{
- switch ( type_ )
- {
- case nullValue:
- return 0;
- case intValue:
- return value_.int_;
- case uintValue:
- JSON_ASSERT_MESSAGE( value_.uint_ <= UInt64(maxInt64), "unsigned integer out of Int64 range" );
- return value_.uint_;
- case realValue:
- JSON_ASSERT_MESSAGE( value_.real_ >= minInt64 && value_.real_ <= maxInt64, "Real out of Int64 range" );
- return Int( value_.real_ );
- case booleanValue:
- return value_.bool_ ? 1 : 0;
- case stringValue:
- case arrayValue:
- case objectValue:
- JSON_FAIL_MESSAGE( "Type is not convertible to Int64" );
- default:
- JSON_ASSERT_UNREACHABLE;
- }
- return 0; // unreachable;
-}
-
-
-Value::UInt64
-Value::asUInt64() const
-{
- switch ( type_ )
- {
- case nullValue:
- return 0;
- case intValue:
- JSON_ASSERT_MESSAGE( value_.int_ >= 0, "Negative integer can not be converted to UInt64" );
- return value_.int_;
- case uintValue:
- return value_.uint_;
- case realValue:
- JSON_ASSERT_MESSAGE( value_.real_ >= 0 && value_.real_ <= maxUInt64, "Real out of UInt64 range" );
- return UInt( value_.real_ );
- case booleanValue:
- return value_.bool_ ? 1 : 0;
- case stringValue:
- case arrayValue:
- case objectValue:
- JSON_FAIL_MESSAGE( "Type is not convertible to UInt64" );
- default:
- JSON_ASSERT_UNREACHABLE;
- }
- return 0; // unreachable;
-}
-# endif // if defined(JSON_HAS_INT64)
-
-
-LargestInt
-Value::asLargestInt() const
-{
-#if defined(JSON_NO_INT64)
- return asInt();
-#else
- return asInt64();
-#endif
-}
-
-
-LargestUInt
-Value::asLargestUInt() const
-{
-#if defined(JSON_NO_INT64)
- return asUInt();
-#else
- return asUInt64();
-#endif
-}
-
-
-double
-Value::asDouble() const
-{
- switch ( type_ )
- {
- case nullValue:
- return 0.0;
- case intValue:
- return static_cast<double>( value_.int_ );
- case uintValue:
-#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
- return static_cast<double>( value_.uint_ );
-#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
- return static_cast<double>( Int(value_.uint_/2) ) * 2 + Int(value_.uint_ & 1);
-#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
- case realValue:
- return value_.real_;
- case booleanValue:
- return value_.bool_ ? 1.0 : 0.0;
- case stringValue:
- case arrayValue:
- case objectValue:
- JSON_FAIL_MESSAGE( "Type is not convertible to double" );
- default:
- JSON_ASSERT_UNREACHABLE;
- }
- return 0; // unreachable;
-}
-
-float
-Value::asFloat() const
-{
- switch ( type_ )
- {
- case nullValue:
- return 0.0f;
- case intValue:
- return static_cast<float>( value_.int_ );
- case uintValue:
-#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
- return static_cast<float>( value_.uint_ );
-#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
- return static_cast<float>( Int(value_.uint_/2) ) * 2 + Int(value_.uint_ & 1);
-#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
- case realValue:
- return static_cast<float>( value_.real_ );
- case booleanValue:
- return value_.bool_ ? 1.0f : 0.0f;
- case stringValue:
- case arrayValue:
- case objectValue:
- JSON_FAIL_MESSAGE( "Type is not convertible to float" );
- default:
- JSON_ASSERT_UNREACHABLE;
- }
- return 0.0f; // unreachable;
-}
-
-bool
-Value::asBool() const
-{
- switch ( type_ )
- {
- case nullValue:
- return false;
- case intValue:
- case uintValue:
- return value_.int_ != 0;
- case realValue:
- return value_.real_ != 0.0;
- case booleanValue:
- return value_.bool_;
- case stringValue:
- return value_.string_ && value_.string_[0] != 0;
- case arrayValue:
- case objectValue:
- return value_.map_->size() != 0;
- default:
- JSON_ASSERT_UNREACHABLE;
- }
- return false; // unreachable;
-}
-
-
-bool
-Value::isConvertibleTo( ValueType other ) const
-{
- switch ( type_ )
- {
- case nullValue:
- return true;
- case intValue:
- return ( other == nullValue && value_.int_ == 0 )
- || other == intValue
- || ( other == uintValue && value_.int_ >= 0 )
- || other == realValue
- || other == stringValue
- || other == booleanValue;
- case uintValue:
- return ( other == nullValue && value_.uint_ == 0 )
- || ( other == intValue && value_.uint_ <= static_cast<unsigned>(maxInt) )
- || other == uintValue
- || other == realValue
- || other == stringValue
- || other == booleanValue;
- case realValue:
- return ( other == nullValue && value_.real_ == 0.0 )
- || ( other == intValue && value_.real_ >= minInt && value_.real_ <= maxInt )
- || ( other == uintValue && value_.real_ >= 0 && value_.real_ <= maxUInt )
- || other == realValue
- || other == stringValue
- || other == booleanValue;
- case booleanValue:
- return ( other == nullValue && value_.bool_ == false )
- || other == intValue
- || other == uintValue
- || other == realValue
- || other == stringValue
- || other == booleanValue;
- case stringValue:
- return other == stringValue
- || ( other == nullValue && (!value_.string_ || value_.string_[0] == 0) );
- case arrayValue:
- return other == arrayValue
- || ( other == nullValue && value_.map_->size() == 0 );
- case objectValue:
- return other == objectValue
- || ( other == nullValue && value_.map_->size() == 0 );
- default:
- JSON_ASSERT_UNREACHABLE;
- }
- return false; // unreachable;
-}
-
-
-/// Number of values in array or object
-ArrayIndex
-Value::size() const
-{
- switch ( type_ )
- {
- case nullValue:
- case intValue:
- case uintValue:
- case realValue:
- case booleanValue:
- case stringValue:
- return 0;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- case arrayValue: // size of the array is highest index + 1
- if ( !value_.map_->empty() )
- {
- ObjectValues::const_iterator itLast = value_.map_->end();
- --itLast;
- return (*itLast).first.index()+1;
- }
- return 0;
- case objectValue:
- return ArrayIndex( value_.map_->size() );
-#else
- case arrayValue:
- return Int( value_.array_->size() );
- case objectValue:
- return Int( value_.map_->size() );
-#endif
- default:
- JSON_ASSERT_UNREACHABLE;
- }
- return 0; // unreachable;
-}
-
-
-bool
-Value::empty() const
-{
- if ( isNull() || isArray() || isObject() )
- return size() == 0u;
- else
- return false;
-}
-
-
-bool
-Value::operator!() const
-{
- return isNull();
-}
-
-
-void
-Value::clear()
-{
- JSON_ASSERT( type_ == nullValue || type_ == arrayValue || type_ == objectValue );
-
- switch ( type_ )
- {
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- case arrayValue:
- case objectValue:
- value_.map_->clear();
- break;
-#else
- case arrayValue:
- value_.array_->clear();
- break;
- case objectValue:
- value_.map_->clear();
- break;
-#endif
- default:
- break;
- }
-}
-
-void
-Value::resize( ArrayIndex newSize )
-{
- JSON_ASSERT( type_ == nullValue || type_ == arrayValue );
- if ( type_ == nullValue )
- *this = Value( arrayValue );
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- ArrayIndex oldSize = size();
- if ( newSize == 0 )
- clear();
- else if ( newSize > oldSize )
- (*this)[ newSize - 1 ];
- else
- {
- for ( ArrayIndex index = newSize; index < oldSize; ++index )
- {
- value_.map_->erase( index );
- }
- assert( size() == newSize );
- }
-#else
- value_.array_->resize( newSize );
-#endif
-}
-
-
-Value &
-Value::operator[]( ArrayIndex index )
-{
- JSON_ASSERT( type_ == nullValue || type_ == arrayValue );
- if ( type_ == nullValue )
- *this = Value( arrayValue );
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- CZString key( index );
- ObjectValues::iterator it = value_.map_->lower_bound( key );
- if ( it != value_.map_->end() && (*it).first == key )
- return (*it).second;
-
- ObjectValues::value_type defaultValue( key, null );
- it = value_.map_->insert( it, defaultValue );
- return (*it).second;
-#else
- return value_.array_->resolveReference( index );
-#endif
-}
-
-
-Value &
-Value::operator[]( int index )
-{
- JSON_ASSERT( index >= 0 );
- return (*this)[ ArrayIndex(index) ];
-}
-
+// Notes: policy_ indicates if the string was allocated when
+// a string is stored.
-const Value &
-Value::operator[]( ArrayIndex index ) const
-{
- JSON_ASSERT( type_ == nullValue || type_ == arrayValue );
- if ( type_ == nullValue )
- return null;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- CZString key( index );
- ObjectValues::const_iterator it = value_.map_->find( key );
- if ( it == value_.map_->end() )
- return null;
- return (*it).second;
-#else
- Value *value = value_.array_->find( index );
- return value ? *value : null;
-#endif
+Value::CZString::CZString(ArrayIndex index) : cstr_(nullptr), index_(index) {}
+
+Value::CZString::CZString(char const* str, unsigned length,
+ DuplicationPolicy allocate)
+ : cstr_(str) {
+ // allocate != duplicate
+ storage_.policy_ = allocate & 0x3;
+ storage_.length_ = length & 0x3FFFFFFF;
+}
+
+Value::CZString::CZString(const CZString& other) {
+ cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != nullptr
+ ? duplicateStringValue(other.cstr_, other.storage_.length_)
+ : other.cstr_);
+ storage_.policy_ =
+ static_cast<unsigned>(
+ other.cstr_
+ ? (static_cast<DuplicationPolicy>(other.storage_.policy_) ==
+ noDuplication
+ ? noDuplication
+ : duplicate)
+ : static_cast<DuplicationPolicy>(other.storage_.policy_)) &
+ 3U;
+ storage_.length_ = other.storage_.length_;
+}
+
+Value::CZString::CZString(CZString&& other) noexcept
+ : cstr_(other.cstr_), index_(other.index_) {
+ other.cstr_ = nullptr;
+}
+
+Value::CZString::~CZString() {
+ if (cstr_ && storage_.policy_ == duplicate) {
+ releaseStringValue(const_cast<char*>(cstr_),
+ storage_.length_ + 1U); // +1 for null terminating
+ // character for sake of
+ // completeness but not actually
+ // necessary
+ }
+}
+
+void Value::CZString::swap(CZString& other) {
+ std::swap(cstr_, other.cstr_);
+ std::swap(index_, other.index_);
+}
+
+Value::CZString& Value::CZString::operator=(const CZString& other) {
+ cstr_ = other.cstr_;
+ index_ = other.index_;
+ return *this;
+}
+
+Value::CZString& Value::CZString::operator=(CZString&& other) noexcept {
+ cstr_ = other.cstr_;
+ index_ = other.index_;
+ other.cstr_ = nullptr;
+ return *this;
+}
+
+bool Value::CZString::operator<(const CZString& other) const {
+ if (!cstr_)
+ return index_ < other.index_;
+ // return strcmp(cstr_, other.cstr_) < 0;
+ // Assume both are strings.
+ unsigned this_len = this->storage_.length_;
+ unsigned other_len = other.storage_.length_;
+ unsigned min_len = std::min<unsigned>(this_len, other_len);
+ JSON_ASSERT(this->cstr_ && other.cstr_);
+ int comp = memcmp(this->cstr_, other.cstr_, min_len);
+ if (comp < 0)
+ return true;
+ if (comp > 0)
+ return false;
+ return (this_len < other_len);
+}
+
+bool Value::CZString::operator==(const CZString& other) const {
+ if (!cstr_)
+ return index_ == other.index_;
+ // return strcmp(cstr_, other.cstr_) == 0;
+ // Assume both are strings.
+ unsigned this_len = this->storage_.length_;
+ unsigned other_len = other.storage_.length_;
+ if (this_len != other_len)
+ return false;
+ JSON_ASSERT(this->cstr_ && other.cstr_);
+ int comp = memcmp(this->cstr_, other.cstr_, this_len);
+ return comp == 0;
+}
+
+ArrayIndex Value::CZString::index() const { return index_; }
+
+// const char* Value::CZString::c_str() const { return cstr_; }
+const char* Value::CZString::data() const { return cstr_; }
+unsigned Value::CZString::length() const { return storage_.length_; }
+bool Value::CZString::isStaticString() const {
+ return storage_.policy_ == noDuplication;
}
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class Value::Value
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
-const Value &
-Value::operator[]( int index ) const
-{
- JSON_ASSERT( index >= 0 );
- return (*this)[ ArrayIndex(index) ];
+/*! \internal Default constructor initialization must be equivalent to:
+ * memset( this, 0, sizeof(Value) )
+ * This optimization is used in ValueInternalMap fast allocator.
+ */
+Value::Value(ValueType type) {
+ static char const emptyString[] = "";
+ initBasic(type);
+ switch (type) {
+ case nullValue:
+ break;
+ case intValue:
+ case uintValue:
+ value_.int_ = 0;
+ break;
+ case realValue:
+ value_.real_ = 0.0;
+ break;
+ case stringValue:
+ // allocated_ == false, so this is safe.
+ value_.string_ = const_cast<char*>(static_cast<char const*>(emptyString));
+ break;
+ case arrayValue:
+ case objectValue:
+ value_.map_ = new ObjectValues();
+ break;
+ case booleanValue:
+ value_.bool_ = false;
+ break;
+ default:
+ JSON_ASSERT_UNREACHABLE;
+ }
+}
+
+Value::Value(Int value) {
+ initBasic(intValue);
+ value_.int_ = value;
+}
+
+Value::Value(UInt value) {
+ initBasic(uintValue);
+ value_.uint_ = value;
}
-
-
-Value &
-Value::operator[]( const char *key )
-{
- return resolveReference( key, false );
+#if defined(JSON_HAS_INT64)
+Value::Value(Int64 value) {
+ initBasic(intValue);
+ value_.int_ = value;
}
-
-
-Value &
-Value::resolveReference( const char *key,
- bool isStatic )
-{
- JSON_ASSERT( type_ == nullValue || type_ == objectValue );
- if ( type_ == nullValue )
- *this = Value( objectValue );
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- CZString actualKey( key, isStatic ? CZString::noDuplication
- : CZString::duplicateOnCopy );
- ObjectValues::iterator it = value_.map_->lower_bound( actualKey );
- if ( it != value_.map_->end() && (*it).first == actualKey )
- return (*it).second;
-
- ObjectValues::value_type defaultValue( actualKey, null );
- it = value_.map_->insert( it, defaultValue );
- Value &value = (*it).second;
- return value;
-#else
- return value_.map_->resolveReference( key, isStatic );
-#endif
+Value::Value(UInt64 value) {
+ initBasic(uintValue);
+ value_.uint_ = value;
}
+#endif // defined(JSON_HAS_INT64)
-
-Value
-Value::get( ArrayIndex index,
- const Value &defaultValue ) const
-{
- const Value *value = &((*this)[index]);
- return value == &null ? defaultValue : *value;
+Value::Value(double value) {
+ initBasic(realValue);
+ value_.real_ = value;
}
-
-bool
-Value::isValidIndex( ArrayIndex index ) const
-{
- return index < size();
+Value::Value(const char* value) {
+ initBasic(stringValue, true);
+ JSON_ASSERT_MESSAGE(value != nullptr,
+ "Null Value Passed to Value Constructor");
+ value_.string_ = duplicateAndPrefixStringValue(
+ value, static_cast<unsigned>(strlen(value)));
}
-
-
-const Value &
-Value::operator[]( const char *key ) const
-{
- JSON_ASSERT( type_ == nullValue || type_ == objectValue );
- if ( type_ == nullValue )
- return null;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- CZString actualKey( key, CZString::noDuplication );
- ObjectValues::const_iterator it = value_.map_->find( actualKey );
- if ( it == value_.map_->end() )
- return null;
- return (*it).second;
-#else
- const Value *value = value_.map_->find( key );
- return value ? *value : null;
-#endif
+Value::Value(const char* begin, const char* end) {
+ initBasic(stringValue, true);
+ value_.string_ =
+ duplicateAndPrefixStringValue(begin, static_cast<unsigned>(end - begin));
}
-
-Value &
-Value::operator[]( const std::string &key )
-{
- return (*this)[ key.c_str() ];
+Value::Value(const String& value) {
+ initBasic(stringValue, true);
+ value_.string_ = duplicateAndPrefixStringValue(
+ value.data(), static_cast<unsigned>(value.length()));
}
-
-const Value &
-Value::operator[]( const std::string &key ) const
-{
- return (*this)[ key.c_str() ];
+Value::Value(const StaticString& value) {
+ initBasic(stringValue);
+ value_.string_ = const_cast<char*>(value.c_str());
}
-Value &
-Value::operator[]( const StaticString &key )
-{
- return resolveReference( key, true );
+Value::Value(bool value) {
+ initBasic(booleanValue);
+ value_.bool_ = value;
}
-
-# ifdef JSON_USE_CPPTL
-Value &
-Value::operator[]( const CppTL::ConstString &key )
-{
- return (*this)[ key.c_str() ];
+Value::Value(const Value& other) {
+ dupPayload(other);
+ dupMeta(other);
}
-
-const Value &
-Value::operator[]( const CppTL::ConstString &key ) const
-{
- return (*this)[ key.c_str() ];
+Value::Value(Value&& other) noexcept {
+ initBasic(nullValue);
+ swap(other);
}
-# endif
-
-Value &
-Value::append( const Value &value )
-{
- return (*this)[size()] = value;
+Value::~Value() {
+ releasePayload();
+ value_.uint_ = 0;
}
+Value& Value::operator=(const Value& other) {
+ Value(other).swap(*this);
+ return *this;
+}
-Value
-Value::get( const char *key,
- const Value &defaultValue ) const
-{
- const Value *value = &((*this)[key]);
- return value == &null ? defaultValue : *value;
+Value& Value::operator=(Value&& other) noexcept {
+ other.swap(*this);
+ return *this;
}
+void Value::swapPayload(Value& other) {
+ std::swap(bits_, other.bits_);
+ std::swap(value_, other.value_);
+}
-Value
-Value::get( const std::string &key,
- const Value &defaultValue ) const
-{
- return get( key.c_str(), defaultValue );
+void Value::copyPayload(const Value& other) {
+ releasePayload();
+ dupPayload(other);
}
-Value
-Value::removeMember( const char* key )
-{
- JSON_ASSERT( type_ == nullValue || type_ == objectValue );
- if ( type_ == nullValue )
- return null;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- CZString actualKey( key, CZString::noDuplication );
- ObjectValues::iterator it = value_.map_->find( actualKey );
- if ( it == value_.map_->end() )
- return null;
- Value old(it->second);
- value_.map_->erase(it);
- return old;
-#else
- Value *value = value_.map_->find( key );
- if (value){
- Value old(*value);
- value_.map_.remove( key );
- return old;
- } else {
- return null;
- }
-#endif
+void Value::swap(Value& other) {
+ swapPayload(other);
+ std::swap(comments_, other.comments_);
+ std::swap(start_, other.start_);
+ std::swap(limit_, other.limit_);
}
-Value
-Value::removeMember( const std::string &key )
-{
- return removeMember( key.c_str() );
+void Value::copy(const Value& other) {
+ copyPayload(other);
+ dupMeta(other);
}
-# ifdef JSON_USE_CPPTL
-Value
-Value::get( const CppTL::ConstString &key,
- const Value &defaultValue ) const
-{
- return get( key.c_str(), defaultValue );
+ValueType Value::type() const {
+ return static_cast<ValueType>(bits_.value_type_);
}
-# endif
-bool
-Value::isMember( const char *key ) const
-{
- const Value *value = &((*this)[key]);
- return value != &null;
+int Value::compare(const Value& other) const {
+ if (*this < other)
+ return -1;
+ if (*this > other)
+ return 1;
+ return 0;
}
+bool Value::operator<(const Value& other) const {
+ int typeDelta = type() - other.type();
+ if (typeDelta)
+ return typeDelta < 0;
+ switch (type()) {
+ case nullValue:
+ return false;
+ case intValue:
+ return value_.int_ < other.value_.int_;
+ case uintValue:
+ return value_.uint_ < other.value_.uint_;
+ case realValue:
+ return value_.real_ < other.value_.real_;
+ case booleanValue:
+ return value_.bool_ < other.value_.bool_;
+ case stringValue: {
+ if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) {
+ return other.value_.string_ != nullptr;
+ }
+ unsigned this_len;
+ unsigned other_len;
+ char const* this_str;
+ char const* other_str;
+ decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
+ &this_str);
+ decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len,
+ &other_str);
+ unsigned min_len = std::min<unsigned>(this_len, other_len);
+ JSON_ASSERT(this_str && other_str);
+ int comp = memcmp(this_str, other_str, min_len);
+ if (comp < 0)
+ return true;
+ if (comp > 0)
+ return false;
+ return (this_len < other_len);
+ }
+ case arrayValue:
+ case objectValue: {
+ auto thisSize = value_.map_->size();
+ auto otherSize = other.value_.map_->size();
+ if (thisSize != otherSize)
+ return thisSize < otherSize;
+ return (*value_.map_) < (*other.value_.map_);
+ }
+ default:
+ JSON_ASSERT_UNREACHABLE;
+ }
+ return false; // unreachable
+}
+
+bool Value::operator<=(const Value& other) const { return !(other < *this); }
+
+bool Value::operator>=(const Value& other) const { return !(*this < other); }
+
+bool Value::operator>(const Value& other) const { return other < *this; }
+
+bool Value::operator==(const Value& other) const {
+ if (type() != other.type())
+ return false;
+ switch (type()) {
+ case nullValue:
+ return true;
+ case intValue:
+ return value_.int_ == other.value_.int_;
+ case uintValue:
+ return value_.uint_ == other.value_.uint_;
+ case realValue:
+ return value_.real_ == other.value_.real_;
+ case booleanValue:
+ return value_.bool_ == other.value_.bool_;
+ case stringValue: {
+ if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) {
+ return (value_.string_ == other.value_.string_);
+ }
+ unsigned this_len;
+ unsigned other_len;
+ char const* this_str;
+ char const* other_str;
+ decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
+ &this_str);
+ decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len,
+ &other_str);
+ if (this_len != other_len)
+ return false;
+ JSON_ASSERT(this_str && other_str);
+ int comp = memcmp(this_str, other_str, this_len);
+ return comp == 0;
+ }
+ case arrayValue:
+ case objectValue:
+ return value_.map_->size() == other.value_.map_->size() &&
+ (*value_.map_) == (*other.value_.map_);
+ default:
+ JSON_ASSERT_UNREACHABLE;
+ }
+ return false; // unreachable
+}
+
+bool Value::operator!=(const Value& other) const { return !(*this == other); }
+
+const char* Value::asCString() const {
+ JSON_ASSERT_MESSAGE(type() == stringValue,
+ "in Json::Value::asCString(): requires stringValue");
+ if (value_.string_ == nullptr)
+ return nullptr;
+ unsigned this_len;
+ char const* this_str;
+ decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
+ &this_str);
+ return this_str;
+}
+
+#if JSONCPP_USING_SECURE_MEMORY
+unsigned Value::getCStringLength() const {
+ JSON_ASSERT_MESSAGE(type() == stringValue,
+ "in Json::Value::asCString(): requires stringValue");
+ if (value_.string_ == 0)
+ return 0;
+ unsigned this_len;
+ char const* this_str;
+ decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
+ &this_str);
+ return this_len;
+}
+#endif
-bool
-Value::isMember( const std::string &key ) const
-{
- return isMember( key.c_str() );
+bool Value::getString(char const** begin, char const** end) const {
+ if (type() != stringValue)
+ return false;
+ if (value_.string_ == nullptr)
+ return false;
+ unsigned length;
+ decodePrefixedString(this->isAllocated(), this->value_.string_, &length,
+ begin);
+ *end = *begin + length;
+ return true;
+}
+
+String Value::asString() const {
+ switch (type()) {
+ case nullValue:
+ return "";
+ case stringValue: {
+ if (value_.string_ == nullptr)
+ return "";
+ unsigned this_len;
+ char const* this_str;
+ decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
+ &this_str);
+ return String(this_str, this_len);
+ }
+ case booleanValue:
+ return value_.bool_ ? "true" : "false";
+ case intValue:
+ return valueToString(value_.int_);
+ case uintValue:
+ return valueToString(value_.uint_);
+ case realValue:
+ return valueToString(value_.real_);
+ default:
+ JSON_FAIL_MESSAGE("Type is not convertible to string");
+ }
+}
+
+Value::Int Value::asInt() const {
+ switch (type()) {
+ case intValue:
+ JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range");
+ return Int(value_.int_);
+ case uintValue:
+ JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range");
+ return Int(value_.uint_);
+ case realValue:
+ JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt),
+ "double out of Int range");
+ return Int(value_.real_);
+ case nullValue:
+ return 0;
+ case booleanValue:
+ return value_.bool_ ? 1 : 0;
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to Int.");
+}
+
+Value::UInt Value::asUInt() const {
+ switch (type()) {
+ case intValue:
+ JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range");
+ return UInt(value_.int_);
+ case uintValue:
+ JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range");
+ return UInt(value_.uint_);
+ case realValue:
+ JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt),
+ "double out of UInt range");
+ return UInt(value_.real_);
+ case nullValue:
+ return 0;
+ case booleanValue:
+ return value_.bool_ ? 1 : 0;
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to UInt.");
}
+#if defined(JSON_HAS_INT64)
-# ifdef JSON_USE_CPPTL
-bool
-Value::isMember( const CppTL::ConstString &key ) const
-{
- return isMember( key.c_str() );
+Value::Int64 Value::asInt64() const {
+ switch (type()) {
+ case intValue:
+ return Int64(value_.int_);
+ case uintValue:
+ JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range");
+ return Int64(value_.uint_);
+ case realValue:
+ JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64),
+ "double out of Int64 range");
+ return Int64(value_.real_);
+ case nullValue:
+ return 0;
+ case booleanValue:
+ return value_.bool_ ? 1 : 0;
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to Int64.");
+}
+
+Value::UInt64 Value::asUInt64() const {
+ switch (type()) {
+ case intValue:
+ JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range");
+ return UInt64(value_.int_);
+ case uintValue:
+ return UInt64(value_.uint_);
+ case realValue:
+ JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64),
+ "double out of UInt64 range");
+ return UInt64(value_.real_);
+ case nullValue:
+ return 0;
+ case booleanValue:
+ return value_.bool_ ? 1 : 0;
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to UInt64.");
}
-#endif
+#endif // if defined(JSON_HAS_INT64)
-Value::Members
-Value::getMemberNames() const
-{
- JSON_ASSERT( type_ == nullValue || type_ == objectValue );
- if ( type_ == nullValue )
- return Value::Members();
- Members members;
- members.reserve( value_.map_->size() );
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- ObjectValues::const_iterator it = value_.map_->begin();
- ObjectValues::const_iterator itEnd = value_.map_->end();
- for ( ; it != itEnd; ++it )
- members.push_back( std::string( (*it).first.c_str() ) );
+LargestInt Value::asLargestInt() const {
+#if defined(JSON_NO_INT64)
+ return asInt();
#else
- ValueInternalMap::IteratorState it;
- ValueInternalMap::IteratorState itEnd;
- value_.map_->makeBeginIterator( it );
- value_.map_->makeEndIterator( itEnd );
- for ( ; !ValueInternalMap::equals( it, itEnd ); ValueInternalMap::increment(it) )
- members.push_back( std::string( ValueInternalMap::key( it ) ) );
+ return asInt64();
#endif
- return members;
}
-//
-//# ifdef JSON_USE_CPPTL
-//EnumMemberNames
-//Value::enumMemberNames() const
-//{
-// if ( type_ == objectValue )
-// {
-// return CppTL::Enum::any( CppTL::Enum::transform(
-// CppTL::Enum::keys( *(value_.map_), CppTL::Type<const CZString &>() ),
-// MemberNamesTransform() ) );
-// }
-// return EnumMemberNames();
-//}
-//
-//
-//EnumValues
-//Value::enumValues() const
-//{
-// if ( type_ == objectValue || type_ == arrayValue )
-// return CppTL::Enum::anyValues( *(value_.map_),
-// CppTL::Type<const Value &>() );
-// return EnumValues();
-//}
-//
-//# endif
-
-bool
-Value::isNull() const
-{
- return type_ == nullValue;
+LargestUInt Value::asLargestUInt() const {
+#if defined(JSON_NO_INT64)
+ return asUInt();
+#else
+ return asUInt64();
+#endif
}
+double Value::asDouble() const {
+ switch (type()) {
+ case intValue:
+ return static_cast<double>(value_.int_);
+ case uintValue:
+#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+ return static_cast<double>(value_.uint_);
+#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+ return integerToDouble(value_.uint_);
+#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+ case realValue:
+ return value_.real_;
+ case nullValue:
+ return 0.0;
+ case booleanValue:
+ return value_.bool_ ? 1.0 : 0.0;
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to double.");
+}
+
+float Value::asFloat() const {
+ switch (type()) {
+ case intValue:
+ return static_cast<float>(value_.int_);
+ case uintValue:
+#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+ return static_cast<float>(value_.uint_);
+#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+ // This can fail (silently?) if the value is bigger than MAX_FLOAT.
+ return static_cast<float>(integerToDouble(value_.uint_));
+#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+ case realValue:
+ return static_cast<float>(value_.real_);
+ case nullValue:
+ return 0.0;
+ case booleanValue:
+ return value_.bool_ ? 1.0F : 0.0F;
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to float.");
+}
+
+bool Value::asBool() const {
+ switch (type()) {
+ case booleanValue:
+ return value_.bool_;
+ case nullValue:
+ return false;
+ case intValue:
+ return value_.int_ != 0;
+ case uintValue:
+ return value_.uint_ != 0;
+ case realValue: {
+ // According to JavaScript language zero or NaN is regarded as false
+ const auto value_classification = std::fpclassify(value_.real_);
+ return value_classification != FP_ZERO && value_classification != FP_NAN;
+ }
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to bool.");
+}
+
+bool Value::isConvertibleTo(ValueType other) const {
+ switch (other) {
+ case nullValue:
+ return (isNumeric() && asDouble() == 0.0) ||
+ (type() == booleanValue && !value_.bool_) ||
+ (type() == stringValue && asString().empty()) ||
+ (type() == arrayValue && value_.map_->empty()) ||
+ (type() == objectValue && value_.map_->empty()) ||
+ type() == nullValue;
+ case intValue:
+ return isInt() ||
+ (type() == realValue && InRange(value_.real_, minInt, maxInt)) ||
+ type() == booleanValue || type() == nullValue;
+ case uintValue:
+ return isUInt() ||
+ (type() == realValue && InRange(value_.real_, 0, maxUInt)) ||
+ type() == booleanValue || type() == nullValue;
+ case realValue:
+ return isNumeric() || type() == booleanValue || type() == nullValue;
+ case booleanValue:
+ return isNumeric() || type() == booleanValue || type() == nullValue;
+ case stringValue:
+ return isNumeric() || type() == booleanValue || type() == stringValue ||
+ type() == nullValue;
+ case arrayValue:
+ return type() == arrayValue || type() == nullValue;
+ case objectValue:
+ return type() == objectValue || type() == nullValue;
+ }
+ JSON_ASSERT_UNREACHABLE;
+ return false;
+}
-bool
-Value::isBool() const
-{
- return type_ == booleanValue;
+/// Number of values in array or object
+ArrayIndex Value::size() const {
+ switch (type()) {
+ case nullValue:
+ case intValue:
+ case uintValue:
+ case realValue:
+ case booleanValue:
+ case stringValue:
+ return 0;
+ case arrayValue: // size of the array is highest index + 1
+ if (!value_.map_->empty()) {
+ ObjectValues::const_iterator itLast = value_.map_->end();
+ --itLast;
+ return (*itLast).first.index() + 1;
+ }
+ return 0;
+ case objectValue:
+ return ArrayIndex(value_.map_->size());
+ }
+ JSON_ASSERT_UNREACHABLE;
+ return 0; // unreachable;
+}
+
+bool Value::empty() const {
+ if (isNull() || isArray() || isObject())
+ return size() == 0U;
+ return false;
+}
+
+Value::operator bool() const { return !isNull(); }
+
+void Value::clear() {
+ JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue ||
+ type() == objectValue,
+ "in Json::Value::clear(): requires complex value");
+ start_ = 0;
+ limit_ = 0;
+ switch (type()) {
+ case arrayValue:
+ case objectValue:
+ value_.map_->clear();
+ break;
+ default:
+ break;
+ }
+}
+
+void Value::resize(ArrayIndex newSize) {
+ JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue,
+ "in Json::Value::resize(): requires arrayValue");
+ if (type() == nullValue)
+ *this = Value(arrayValue);
+ ArrayIndex oldSize = size();
+ if (newSize == 0)
+ clear();
+ else if (newSize > oldSize)
+ for (ArrayIndex i = oldSize; i < newSize; ++i)
+ (*this)[i];
+ else {
+ for (ArrayIndex index = newSize; index < oldSize; ++index) {
+ value_.map_->erase(index);
+ }
+ JSON_ASSERT(size() == newSize);
+ }
+}
+
+Value& Value::operator[](ArrayIndex index) {
+ JSON_ASSERT_MESSAGE(
+ type() == nullValue || type() == arrayValue,
+ "in Json::Value::operator[](ArrayIndex): requires arrayValue");
+ if (type() == nullValue)
+ *this = Value(arrayValue);
+ CZString key(index);
+ auto it = value_.map_->lower_bound(key);
+ if (it != value_.map_->end() && (*it).first == key)
+ return (*it).second;
+
+ ObjectValues::value_type defaultValue(key, nullSingleton());
+ it = value_.map_->insert(it, defaultValue);
+ return (*it).second;
+}
+
+Value& Value::operator[](int index) {
+ JSON_ASSERT_MESSAGE(
+ index >= 0,
+ "in Json::Value::operator[](int index): index cannot be negative");
+ return (*this)[ArrayIndex(index)];
+}
+
+const Value& Value::operator[](ArrayIndex index) const {
+ JSON_ASSERT_MESSAGE(
+ type() == nullValue || type() == arrayValue,
+ "in Json::Value::operator[](ArrayIndex)const: requires arrayValue");
+ if (type() == nullValue)
+ return nullSingleton();
+ CZString key(index);
+ ObjectValues::const_iterator it = value_.map_->find(key);
+ if (it == value_.map_->end())
+ return nullSingleton();
+ return (*it).second;
+}
+
+const Value& Value::operator[](int index) const {
+ JSON_ASSERT_MESSAGE(
+ index >= 0,
+ "in Json::Value::operator[](int index) const: index cannot be negative");
+ return (*this)[ArrayIndex(index)];
+}
+
+void Value::initBasic(ValueType type, bool allocated) {
+ setType(type);
+ setIsAllocated(allocated);
+ comments_ = Comments{};
+ start_ = 0;
+ limit_ = 0;
+}
+
+void Value::dupPayload(const Value& other) {
+ setType(other.type());
+ setIsAllocated(false);
+ switch (type()) {
+ case nullValue:
+ case intValue:
+ case uintValue:
+ case realValue:
+ case booleanValue:
+ value_ = other.value_;
+ break;
+ case stringValue:
+ if (other.value_.string_ && other.isAllocated()) {
+ unsigned len;
+ char const* str;
+ decodePrefixedString(other.isAllocated(), other.value_.string_, &len,
+ &str);
+ value_.string_ = duplicateAndPrefixStringValue(str, len);
+ setIsAllocated(true);
+ } else {
+ value_.string_ = other.value_.string_;
+ }
+ break;
+ case arrayValue:
+ case objectValue:
+ value_.map_ = new ObjectValues(*other.value_.map_);
+ break;
+ default:
+ JSON_ASSERT_UNREACHABLE;
+ }
+}
+
+void Value::releasePayload() {
+ switch (type()) {
+ case nullValue:
+ case intValue:
+ case uintValue:
+ case realValue:
+ case booleanValue:
+ break;
+ case stringValue:
+ if (isAllocated())
+ releasePrefixedStringValue(value_.string_);
+ break;
+ case arrayValue:
+ case objectValue:
+ delete value_.map_;
+ break;
+ default:
+ JSON_ASSERT_UNREACHABLE;
+ }
+}
+
+void Value::dupMeta(const Value& other) {
+ comments_ = other.comments_;
+ start_ = other.start_;
+ limit_ = other.limit_;
+}
+
+// Access an object value by name, create a null member if it does not exist.
+// @pre Type of '*this' is object or null.
+// @param key is null-terminated.
+Value& Value::resolveReference(const char* key) {
+ JSON_ASSERT_MESSAGE(
+ type() == nullValue || type() == objectValue,
+ "in Json::Value::resolveReference(): requires objectValue");
+ if (type() == nullValue)
+ *this = Value(objectValue);
+ CZString actualKey(key, static_cast<unsigned>(strlen(key)),
+ CZString::noDuplication); // NOTE!
+ auto it = value_.map_->lower_bound(actualKey);
+ if (it != value_.map_->end() && (*it).first == actualKey)
+ return (*it).second;
+
+ ObjectValues::value_type defaultValue(actualKey, nullSingleton());
+ it = value_.map_->insert(it, defaultValue);
+ Value& value = (*it).second;
+ return value;
+}
+
+// @param key is not null-terminated.
+Value& Value::resolveReference(char const* key, char const* end) {
+ JSON_ASSERT_MESSAGE(
+ type() == nullValue || type() == objectValue,
+ "in Json::Value::resolveReference(key, end): requires objectValue");
+ if (type() == nullValue)
+ *this = Value(objectValue);
+ CZString actualKey(key, static_cast<unsigned>(end - key),
+ CZString::duplicateOnCopy);
+ auto it = value_.map_->lower_bound(actualKey);
+ if (it != value_.map_->end() && (*it).first == actualKey)
+ return (*it).second;
+
+ ObjectValues::value_type defaultValue(actualKey, nullSingleton());
+ it = value_.map_->insert(it, defaultValue);
+ Value& value = (*it).second;
+ return value;
+}
+
+Value Value::get(ArrayIndex index, const Value& defaultValue) const {
+ const Value* value = &((*this)[index]);
+ return value == &nullSingleton() ? defaultValue : *value;
+}
+
+bool Value::isValidIndex(ArrayIndex index) const { return index < size(); }
+
+Value const* Value::find(char const* begin, char const* end) const {
+ JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
+ "in Json::Value::find(begin, end): requires "
+ "objectValue or nullValue");
+ if (type() == nullValue)
+ return nullptr;
+ CZString actualKey(begin, static_cast<unsigned>(end - begin),
+ CZString::noDuplication);
+ ObjectValues::const_iterator it = value_.map_->find(actualKey);
+ if (it == value_.map_->end())
+ return nullptr;
+ return &(*it).second;
+}
+Value* Value::demand(char const* begin, char const* end) {
+ JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
+ "in Json::Value::demand(begin, end): requires "
+ "objectValue or nullValue");
+ return &resolveReference(begin, end);
+}
+const Value& Value::operator[](const char* key) const {
+ Value const* found = find(key, key + strlen(key));
+ if (!found)
+ return nullSingleton();
+ return *found;
+}
+Value const& Value::operator[](const String& key) const {
+ Value const* found = find(key.data(), key.data() + key.length());
+ if (!found)
+ return nullSingleton();
+ return *found;
+}
+
+Value& Value::operator[](const char* key) {
+ return resolveReference(key, key + strlen(key));
+}
+
+Value& Value::operator[](const String& key) {
+ return resolveReference(key.data(), key.data() + key.length());
+}
+
+Value& Value::operator[](const StaticString& key) {
+ return resolveReference(key.c_str());
+}
+
+Value& Value::append(const Value& value) { return append(Value(value)); }
+
+Value& Value::append(Value&& value) {
+ JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue,
+ "in Json::Value::append: requires arrayValue");
+ if (type() == nullValue) {
+ *this = Value(arrayValue);
+ }
+ return this->value_.map_->emplace(size(), std::move(value)).first->second;
+}
+
+bool Value::insert(ArrayIndex index, const Value& newValue) {
+ return insert(index, Value(newValue));
+}
+
+bool Value::insert(ArrayIndex index, Value&& newValue) {
+ JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue,
+ "in Json::Value::insert: requires arrayValue");
+ ArrayIndex length = size();
+ if (index > length) {
+ return false;
+ }
+ for (ArrayIndex i = length; i > index; i--) {
+ (*this)[i] = std::move((*this)[i - 1]);
+ }
+ (*this)[index] = std::move(newValue);
+ return true;
+}
+
+Value Value::get(char const* begin, char const* end,
+ Value const& defaultValue) const {
+ Value const* found = find(begin, end);
+ return !found ? defaultValue : *found;
+}
+Value Value::get(char const* key, Value const& defaultValue) const {
+ return get(key, key + strlen(key), defaultValue);
+}
+Value Value::get(String const& key, Value const& defaultValue) const {
+ return get(key.data(), key.data() + key.length(), defaultValue);
+}
+
+bool Value::removeMember(const char* begin, const char* end, Value* removed) {
+ if (type() != objectValue) {
+ return false;
+ }
+ CZString actualKey(begin, static_cast<unsigned>(end - begin),
+ CZString::noDuplication);
+ auto it = value_.map_->find(actualKey);
+ if (it == value_.map_->end())
+ return false;
+ if (removed)
+ *removed = std::move(it->second);
+ value_.map_->erase(it);
+ return true;
+}
+bool Value::removeMember(const char* key, Value* removed) {
+ return removeMember(key, key + strlen(key), removed);
+}
+bool Value::removeMember(String const& key, Value* removed) {
+ return removeMember(key.data(), key.data() + key.length(), removed);
+}
+void Value::removeMember(const char* key) {
+ JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
+ "in Json::Value::removeMember(): requires objectValue");
+ if (type() == nullValue)
+ return;
+
+ CZString actualKey(key, unsigned(strlen(key)), CZString::noDuplication);
+ value_.map_->erase(actualKey);
+}
+void Value::removeMember(const String& key) { removeMember(key.c_str()); }
+
+bool Value::removeIndex(ArrayIndex index, Value* removed) {
+ if (type() != arrayValue) {
+ return false;
+ }
+ CZString key(index);
+ auto it = value_.map_->find(key);
+ if (it == value_.map_->end()) {
+ return false;
+ }
+ if (removed)
+ *removed = it->second;
+ ArrayIndex oldSize = size();
+ // shift left all items left, into the place of the "removed"
+ for (ArrayIndex i = index; i < (oldSize - 1); ++i) {
+ CZString keey(i);
+ (*value_.map_)[keey] = (*this)[i + 1];
+ }
+ // erase the last one ("leftover")
+ CZString keyLast(oldSize - 1);
+ auto itLast = value_.map_->find(keyLast);
+ value_.map_->erase(itLast);
+ return true;
+}
+
+bool Value::isMember(char const* begin, char const* end) const {
+ Value const* value = find(begin, end);
+ return nullptr != value;
+}
+bool Value::isMember(char const* key) const {
+ return isMember(key, key + strlen(key));
+}
+bool Value::isMember(String const& key) const {
+ return isMember(key.data(), key.data() + key.length());
+}
+
+Value::Members Value::getMemberNames() const {
+ JSON_ASSERT_MESSAGE(
+ type() == nullValue || type() == objectValue,
+ "in Json::Value::getMemberNames(), value must be objectValue");
+ if (type() == nullValue)
+ return Value::Members();
+ Members members;
+ members.reserve(value_.map_->size());
+ ObjectValues::const_iterator it = value_.map_->begin();
+ ObjectValues::const_iterator itEnd = value_.map_->end();
+ for (; it != itEnd; ++it) {
+ members.push_back(String((*it).first.data(), (*it).first.length()));
+ }
+ return members;
+}
+
+static bool IsIntegral(double d) {
+ double integral_part;
+ return modf(d, &integral_part) == 0.0;
+}
+
+bool Value::isNull() const { return type() == nullValue; }
+
+bool Value::isBool() const { return type() == booleanValue; }
+
+bool Value::isInt() const {
+ switch (type()) {
+ case intValue:
+#if defined(JSON_HAS_INT64)
+ return value_.int_ >= minInt && value_.int_ <= maxInt;
+#else
+ return true;
+#endif
+ case uintValue:
+ return value_.uint_ <= UInt(maxInt);
+ case realValue:
+ return value_.real_ >= minInt && value_.real_ <= maxInt &&
+ IsIntegral(value_.real_);
+ default:
+ break;
+ }
+ return false;
+}
+
+bool Value::isUInt() const {
+ switch (type()) {
+ case intValue:
+#if defined(JSON_HAS_INT64)
+ return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt);
+#else
+ return value_.int_ >= 0;
+#endif
+ case uintValue:
+#if defined(JSON_HAS_INT64)
+ return value_.uint_ <= maxUInt;
+#else
+ return true;
+#endif
+ case realValue:
+ return value_.real_ >= 0 && value_.real_ <= maxUInt &&
+ IsIntegral(value_.real_);
+ default:
+ break;
+ }
+ return false;
}
+bool Value::isInt64() const {
+#if defined(JSON_HAS_INT64)
+ switch (type()) {
+ case intValue:
+ return true;
+ case uintValue:
+ return value_.uint_ <= UInt64(maxInt64);
+ case realValue:
+ // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a
+ // double, so double(maxInt64) will be rounded up to 2^63. Therefore we
+ // require the value to be strictly less than the limit.
+ return value_.real_ >= double(minInt64) &&
+ value_.real_ < double(maxInt64) && IsIntegral(value_.real_);
+ default:
+ break;
+ }
+#endif // JSON_HAS_INT64
+ return false;
+}
+
+bool Value::isUInt64() const {
+#if defined(JSON_HAS_INT64)
+ switch (type()) {
+ case intValue:
+ return value_.int_ >= 0;
+ case uintValue:
+ return true;
+ case realValue:
+ // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
+ // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
+ // require the value to be strictly less than the limit.
+ return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble &&
+ IsIntegral(value_.real_);
+ default:
+ break;
+ }
+#endif // JSON_HAS_INT64
+ return false;
+}
+
+bool Value::isIntegral() const {
+ switch (type()) {
+ case intValue:
+ case uintValue:
+ return true;
+ case realValue:
+#if defined(JSON_HAS_INT64)
+ // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
+ // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
+ // require the value to be strictly less than the limit.
+ return value_.real_ >= double(minInt64) &&
+ value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_);
+#else
+ return value_.real_ >= minInt && value_.real_ <= maxUInt &&
+ IsIntegral(value_.real_);
+#endif // JSON_HAS_INT64
+ default:
+ break;
+ }
+ return false;
+}
-bool
-Value::isInt() const
-{
- return type_ == intValue;
+bool Value::isDouble() const {
+ return type() == intValue || type() == uintValue || type() == realValue;
}
+bool Value::isNumeric() const { return isDouble(); }
-bool
-Value::isUInt() const
-{
- return type_ == uintValue;
-}
+bool Value::isString() const { return type() == stringValue; }
+bool Value::isArray() const { return type() == arrayValue; }
-bool
-Value::isIntegral() const
-{
- return type_ == intValue
- || type_ == uintValue
- || type_ == booleanValue;
-}
+bool Value::isObject() const { return type() == objectValue; }
+Value::Comments::Comments(const Comments& that)
+ : ptr_{cloneUnique(that.ptr_)} {}
-bool
-Value::isDouble() const
-{
- return type_ == realValue;
-}
+Value::Comments::Comments(Comments&& that) noexcept
+ : ptr_{std::move(that.ptr_)} {}
+Value::Comments& Value::Comments::operator=(const Comments& that) {
+ ptr_ = cloneUnique(that.ptr_);
+ return *this;
+}
-bool
-Value::isNumeric() const
-{
- return isIntegral() || isDouble();
+Value::Comments& Value::Comments::operator=(Comments&& that) noexcept {
+ ptr_ = std::move(that.ptr_);
+ return *this;
}
+bool Value::Comments::has(CommentPlacement slot) const {
+ return ptr_ && !(*ptr_)[slot].empty();
+}
-bool
-Value::isString() const
-{
- return type_ == stringValue;
+String Value::Comments::get(CommentPlacement slot) const {
+ if (!ptr_)
+ return {};
+ return (*ptr_)[slot];
}
+void Value::Comments::set(CommentPlacement slot, String comment) {
+ if (slot >= CommentPlacement::numberOfCommentPlacement)
+ return;
+ if (!ptr_)
+ ptr_ = std::unique_ptr<Array>(new Array());
+ (*ptr_)[slot] = std::move(comment);
+}
-bool
-Value::isArray() const
-{
- return type_ == nullValue || type_ == arrayValue;
+void Value::setComment(String comment, CommentPlacement placement) {
+ if (!comment.empty() && (comment.back() == '\n')) {
+ // Always discard trailing newline, to aid indentation.
+ comment.pop_back();
+ }
+ JSON_ASSERT(!comment.empty());
+ JSON_ASSERT_MESSAGE(
+ comment[0] == '\0' || comment[0] == '/',
+ "in Json::Value::setComment(): Comments must start with /");
+ comments_.set(placement, std::move(comment));
}
+bool Value::hasComment(CommentPlacement placement) const {
+ return comments_.has(placement);
+}
-bool
-Value::isObject() const
-{
- return type_ == nullValue || type_ == objectValue;
+String Value::getComment(CommentPlacement placement) const {
+ return comments_.get(placement);
}
+void Value::setOffsetStart(ptrdiff_t start) { start_ = start; }
-void
-Value::setComment( const char *comment,
- CommentPlacement placement )
-{
- if ( !comments_ )
- comments_ = new CommentInfo[numberOfCommentPlacement];
- comments_[placement].setComment( comment );
-}
+void Value::setOffsetLimit(ptrdiff_t limit) { limit_ = limit; }
+ptrdiff_t Value::getOffsetStart() const { return start_; }
-void
-Value::setComment( const std::string &comment,
- CommentPlacement placement )
-{
- setComment( comment.c_str(), placement );
-}
+ptrdiff_t Value::getOffsetLimit() const { return limit_; }
+String Value::toStyledString() const {
+ StreamWriterBuilder builder;
-bool
-Value::hasComment( CommentPlacement placement ) const
-{
- return comments_ != nullptr && comments_[placement].comment_ != nullptr;
-}
+ String out = this->hasComment(commentBefore) ? "\n" : "";
+ out += Json::writeString(builder, *this);
+ out += '\n';
-std::string
-Value::getComment( CommentPlacement placement ) const
-{
- if ( hasComment(placement) )
- return comments_[placement].comment_;
- return "";
+ return out;
}
-
-std::string
-Value::toStyledString() const
-{
- StyledWriter writer;
- return writer.write( *this );
+Value::const_iterator Value::begin() const {
+ switch (type()) {
+ case arrayValue:
+ case objectValue:
+ if (value_.map_)
+ return const_iterator(value_.map_->begin());
+ break;
+ default:
+ break;
+ }
+ return {};
}
+Value::const_iterator Value::end() const {
+ switch (type()) {
+ case arrayValue:
+ case objectValue:
+ if (value_.map_)
+ return const_iterator(value_.map_->end());
+ break;
+ default:
+ break;
+ }
+ return {};
+}
-Value::const_iterator
-Value::begin() const
-{
- switch ( type_ )
- {
-#ifdef JSON_VALUE_USE_INTERNAL_MAP
- case arrayValue:
- if ( value_.array_ )
- {
- ValueInternalArray::IteratorState it;
- value_.array_->makeBeginIterator( it );
- return const_iterator( it );
- }
- break;
- case objectValue:
- if ( value_.map_ )
- {
- ValueInternalMap::IteratorState it;
- value_.map_->makeBeginIterator( it );
- return const_iterator( it );
- }
- break;
-#else
- case arrayValue:
- case objectValue:
- if ( value_.map_ )
- return const_iterator( value_.map_->begin() );
- break;
-#endif
- default:
- break;
- }
- return const_iterator();
-}
-
-Value::const_iterator
-Value::end() const
-{
- switch ( type_ )
- {
-#ifdef JSON_VALUE_USE_INTERNAL_MAP
- case arrayValue:
- if ( value_.array_ )
- {
- ValueInternalArray::IteratorState it;
- value_.array_->makeEndIterator( it );
- return const_iterator( it );
- }
- break;
- case objectValue:
- if ( value_.map_ )
- {
- ValueInternalMap::IteratorState it;
- value_.map_->makeEndIterator( it );
- return const_iterator( it );
- }
- break;
-#else
- case arrayValue:
- case objectValue:
- if ( value_.map_ )
- return const_iterator( value_.map_->end() );
- break;
-#endif
- default:
- break;
- }
- return const_iterator();
-}
-
-
-Value::iterator
-Value::begin()
-{
- switch ( type_ )
- {
-#ifdef JSON_VALUE_USE_INTERNAL_MAP
- case arrayValue:
- if ( value_.array_ )
- {
- ValueInternalArray::IteratorState it;
- value_.array_->makeBeginIterator( it );
- return iterator( it );
- }
- break;
- case objectValue:
- if ( value_.map_ )
- {
- ValueInternalMap::IteratorState it;
- value_.map_->makeBeginIterator( it );
- return iterator( it );
- }
- break;
-#else
- case arrayValue:
- case objectValue:
- if ( value_.map_ )
- return iterator( value_.map_->begin() );
- break;
-#endif
- default:
- break;
- }
- return iterator();
-}
-
-Value::iterator
-Value::end()
-{
- switch ( type_ )
- {
-#ifdef JSON_VALUE_USE_INTERNAL_MAP
- case arrayValue:
- if ( value_.array_ )
- {
- ValueInternalArray::IteratorState it;
- value_.array_->makeEndIterator( it );
- return iterator( it );
- }
- break;
- case objectValue:
- if ( value_.map_ )
- {
- ValueInternalMap::IteratorState it;
- value_.map_->makeEndIterator( it );
- return iterator( it );
- }
- break;
-#else
- case arrayValue:
- case objectValue:
- if ( value_.map_ )
- return iterator( value_.map_->end() );
- break;
-#endif
- default:
- break;
- }
- return iterator();
+Value::iterator Value::begin() {
+ switch (type()) {
+ case arrayValue:
+ case objectValue:
+ if (value_.map_)
+ return iterator(value_.map_->begin());
+ break;
+ default:
+ break;
+ }
+ return iterator();
}
+Value::iterator Value::end() {
+ switch (type()) {
+ case arrayValue:
+ case objectValue:
+ if (value_.map_)
+ return iterator(value_.map_->end());
+ break;
+ default:
+ break;
+ }
+ return iterator();
+}
// class PathArgument
// //////////////////////////////////////////////////////////////////
-PathArgument::PathArgument()
- : kind_( kindNone )
-{
-}
-
-
-PathArgument::PathArgument( ArrayIndex index )
- : index_( index )
- , kind_( kindIndex )
-{
-}
-
+PathArgument::PathArgument() = default;
-PathArgument::PathArgument( const char *key )
- : key_( key )
- , kind_( kindKey )
-{
-}
+PathArgument::PathArgument(ArrayIndex index)
+ : index_(index), kind_(kindIndex) {}
+PathArgument::PathArgument(const char* key) : key_(key), kind_(kindKey) {}
-PathArgument::PathArgument( const std::string &key )
- : key_( key.c_str() )
- , kind_( kindKey )
-{
-}
+PathArgument::PathArgument(String key) : key_(std::move(key)), kind_(kindKey) {}
// class Path
// //////////////////////////////////////////////////////////////////
-Path::Path( const std::string &path,
- const PathArgument &a1,
- const PathArgument &a2,
- const PathArgument &a3,
- const PathArgument &a4,
- const PathArgument &a5 )
-{
- InArgs in;
- in.push_back( &a1 );
- in.push_back( &a2 );
- in.push_back( &a3 );
- in.push_back( &a4 );
- in.push_back( &a5 );
- makePath( path, in );
-}
-
-
-void
-Path::makePath( const std::string &path,
- const InArgs &in )
-{
- const char *current = path.c_str();
- const char *end = current + path.length();
- InArgs::const_iterator itInArg = in.begin();
- while ( current != end )
- {
- if ( *current == '[' )
- {
- ++current;
- if ( *current == '%' )
- addPathInArg( path, in, itInArg, PathArgument::kindIndex );
- else
- {
- ArrayIndex index = 0;
- for ( ; current != end && *current >= '0' && *current <= '9'; ++current )
- index = index * 10 + ArrayIndex(*current - '0');
- args_.push_back( index );
- }
- if ( current == end || *current++ != ']' )
- invalidPath( path, int(current - path.c_str()) );
- }
- else if ( *current == '%' )
- {
- addPathInArg( path, in, itInArg, PathArgument::kindKey );
- ++current;
- }
- else if ( *current == '.' )
- {
- ++current;
- }
- else
- {
- const char *beginName = current;
- while ( current != end && !strchr( "[.", *current ) )
- ++current;
- args_.push_back( std::string( beginName, current ) );
- }
- }
-}
-
-
-void
-Path::addPathInArg( const std::string &/*path*/,
- const InArgs &in,
- InArgs::const_iterator &itInArg,
- PathArgument::Kind kind )
-{
- if ( itInArg == in.end() )
- {
- // Error: missing argument %d
- }
- else if ( (*itInArg)->kind_ != kind )
- {
- // Error: bad argument type
- }
- else
- {
- args_.push_back( **itInArg );
- }
-}
-
-
-void
-Path::invalidPath( const std::string &/*path*/,
- int /*location*/ )
-{
- // Error: invalid path.
-}
-
-
-const Value &
-Path::resolve( const Value &root ) const
-{
- const Value *node = &root;
- for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it )
- {
- const PathArgument &arg = *it;
- if ( arg.kind_ == PathArgument::kindIndex )
- {
- if ( !node->isArray() || node->isValidIndex( arg.index_ ) )
- {
- // Error: unable to resolve path (array value expected at position...
- }
- node = &((*node)[arg.index_]);
+Path::Path(const String& path, const PathArgument& a1, const PathArgument& a2,
+ const PathArgument& a3, const PathArgument& a4,
+ const PathArgument& a5) {
+ InArgs in;
+ in.reserve(5);
+ in.push_back(&a1);
+ in.push_back(&a2);
+ in.push_back(&a3);
+ in.push_back(&a4);
+ in.push_back(&a5);
+ makePath(path, in);
+}
+
+void Path::makePath(const String& path, const InArgs& in) {
+ const char* current = path.c_str();
+ const char* end = current + path.length();
+ auto itInArg = in.begin();
+ while (current != end) {
+ if (*current == '[') {
+ ++current;
+ if (*current == '%')
+ addPathInArg(path, in, itInArg, PathArgument::kindIndex);
+ else {
+ ArrayIndex index = 0;
+ for (; current != end && *current >= '0' && *current <= '9'; ++current)
+ index = index * 10 + ArrayIndex(*current - '0');
+ args_.push_back(index);
}
- else if ( arg.kind_ == PathArgument::kindKey )
- {
- if ( !node->isObject() )
- {
- // Error: unable to resolve path (object value expected at position...)
- }
- node = &((*node)[arg.key_]);
- if ( node == &Value::null )
- {
- // Error: unable to resolve path (object has no member named '' at position...)
- }
+ if (current == end || *++current != ']')
+ invalidPath(path, int(current - path.c_str()));
+ } else if (*current == '%') {
+ addPathInArg(path, in, itInArg, PathArgument::kindKey);
+ ++current;
+ } else if (*current == '.' || *current == ']') {
+ ++current;
+ } else {
+ const char* beginName = current;
+ while (current != end && !strchr("[.", *current))
+ ++current;
+ args_.push_back(String(beginName, current));
+ }
+ }
+}
+
+void Path::addPathInArg(const String& /*path*/, const InArgs& in,
+ InArgs::const_iterator& itInArg,
+ PathArgument::Kind kind) {
+ if (itInArg == in.end()) {
+ // Error: missing argument %d
+ } else if ((*itInArg)->kind_ != kind) {
+ // Error: bad argument type
+ } else {
+ args_.push_back(**itInArg++);
+ }
+}
+
+void Path::invalidPath(const String& /*path*/, int /*location*/) {
+ // Error: invalid path.
+}
+
+const Value& Path::resolve(const Value& root) const {
+ const Value* node = &root;
+ for (const auto& arg : args_) {
+ if (arg.kind_ == PathArgument::kindIndex) {
+ if (!node->isArray() || !node->isValidIndex(arg.index_)) {
+ // Error: unable to resolve path (array value expected at position... )
+ return Value::nullSingleton();
}
- }
- return *node;
-}
-
-
-Value
-Path::resolve( const Value &root,
- const Value &defaultValue ) const
-{
- const Value *node = &root;
- for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it )
- {
- const PathArgument &arg = *it;
- if ( arg.kind_ == PathArgument::kindIndex )
- {
- if ( !node->isArray() || node->isValidIndex( arg.index_ ) )
- return defaultValue;
- node = &((*node)[arg.index_]);
+ node = &((*node)[arg.index_]);
+ } else if (arg.kind_ == PathArgument::kindKey) {
+ if (!node->isObject()) {
+ // Error: unable to resolve path (object value expected at position...)
+ return Value::nullSingleton();
}
- else if ( arg.kind_ == PathArgument::kindKey )
- {
- if ( !node->isObject() )
- return defaultValue;
- node = &((*node)[arg.key_]);
- if ( node == &Value::null )
- return defaultValue;
+ node = &((*node)[arg.key_]);
+ if (node == &Value::nullSingleton()) {
+ // Error: unable to resolve path (object has no member named '' at
+ // position...)
+ return Value::nullSingleton();
}
- }
- return *node;
-}
-
-
-Value &
-Path::make( Value &root ) const
-{
- Value *node = &root;
- for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it )
- {
- const PathArgument &arg = *it;
- if ( arg.kind_ == PathArgument::kindIndex )
- {
- if ( !node->isArray() )
- {
- // Error: node is not an array at position ...
- }
- node = &((*node)[arg.index_]);
+ }
+ }
+ return *node;
+}
+
+Value Path::resolve(const Value& root, const Value& defaultValue) const {
+ const Value* node = &root;
+ for (const auto& arg : args_) {
+ if (arg.kind_ == PathArgument::kindIndex) {
+ if (!node->isArray() || !node->isValidIndex(arg.index_))
+ return defaultValue;
+ node = &((*node)[arg.index_]);
+ } else if (arg.kind_ == PathArgument::kindKey) {
+ if (!node->isObject())
+ return defaultValue;
+ node = &((*node)[arg.key_]);
+ if (node == &Value::nullSingleton())
+ return defaultValue;
+ }
+ }
+ return *node;
+}
+
+Value& Path::make(Value& root) const {
+ Value* node = &root;
+ for (const auto& arg : args_) {
+ if (arg.kind_ == PathArgument::kindIndex) {
+ if (!node->isArray()) {
+ // Error: node is not an array at position ...
}
- else if ( arg.kind_ == PathArgument::kindKey )
- {
- if ( !node->isObject() )
- {
- // Error: node is not an object at position...
- }
- node = &((*node)[arg.key_]);
+ node = &((*node)[arg.index_]);
+ } else if (arg.kind_ == PathArgument::kindKey) {
+ if (!node->isObject()) {
+ // Error: node is not an object at position...
}
- }
- return *node;
+ node = &((*node)[arg.key_]);
+ }
+ }
+ return *node;
}
-
} // namespace Json
// //////////////////////////////////////////////////////////////////////
// End of content of file: src/lib_json/json_value.cpp
// //////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: src/lib_json/json_writer.cpp
// //////////////////////////////////////////////////////////////////////
-// Copyright 2007-2010 Baptiste Lepilleur
+// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#if !defined(JSON_IS_AMALGAMATION)
-# include <json/writer.h>
-# include "json_tool.h"
+#include "json_tool.h"
+#include <json/writer.h>
#endif // if !defined(JSON_IS_AMALGAMATION)
-#include <utility>
-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-#include <iostream>
-#include <sstream>
+#include <algorithm>
+#include <cassert>
+#include <cctype>
+#include <cstring>
#include <iomanip>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <utility>
+
+#if __cplusplus >= 201103L
+#include <cmath>
+#include <cstdio>
-#if _MSC_VER >= 1400 // VC++ 8.0
-#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated.
+#if !defined(isnan)
+#define isnan std::isnan
#endif
-namespace Json {
+#if !defined(isfinite)
+#define isfinite std::isfinite
+#endif
-static bool containsControlCharacter( const char* str )
-{
- while ( *str )
- {
- if ( isControlCharacter( *(str++) ) )
- return true;
- }
- return false;
-}
+#else
+#include <cmath>
+#include <cstdio>
+#if defined(_MSC_VER)
+#if !defined(isnan)
+#include <float.h>
+#define isnan _isnan
+#endif
-std::string valueToString( LargestInt value )
-{
- UIntToStringBuffer buffer;
- char *current = buffer + sizeof(buffer);
- bool isNegative = value < 0;
- if ( isNegative )
- value = -value;
- uintToString( LargestUInt(value), current );
- if ( isNegative )
- *--current = '-';
- assert( current >= buffer );
- return current;
-}
+#if !defined(isfinite)
+#include <float.h>
+#define isfinite _finite
+#endif
+#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
+#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
+#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
-std::string valueToString( LargestUInt value )
-{
- UIntToStringBuffer buffer;
- char *current = buffer + sizeof(buffer);
- uintToString( value, current );
- assert( current >= buffer );
- return current;
-}
+#endif //_MSC_VER
-#if defined(JSON_HAS_INT64)
+#if defined(__sun) && defined(__SVR4) // Solaris
+#if !defined(isfinite)
+#include <ieeefp.h>
+#define isfinite finite
+#endif
+#endif
-std::string valueToString( Int value )
-{
- return valueToString( LargestInt(value) );
-}
+#if defined(__hpux)
+#if !defined(isfinite)
+#if defined(__ia64) && !defined(finite)
+#define isfinite(x) \
+ ((sizeof(x) == sizeof(float) ? _Isfinitef(x) : _IsFinite(x)))
+#endif
+#endif
+#endif
+#if !defined(isnan)
+// IEEE standard states that NaN values will not compare to themselves
+#define isnan(x) ((x) != (x))
+#endif
-std::string valueToString( UInt value )
-{
- return valueToString( LargestUInt(value) );
-}
+#if !defined(__APPLE__)
+#if !defined(isfinite)
+#define isfinite finite
+#endif
+#endif
+#endif
-#endif // # if defined(JSON_HAS_INT64)
+#if defined(_MSC_VER)
+// Disable warning about strdup being deprecated.
+#pragma warning(disable : 4996)
+#endif
+namespace Json {
-std::string valueToString( double value )
-{
- char buffer[32];
-#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning.
- sprintf_s(buffer, sizeof(buffer), "%#.16g", value);
+#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
+using StreamWriterPtr = std::unique_ptr<StreamWriter>;
#else
- sprintf(buffer, "%#.16g", value);
+using StreamWriterPtr = std::auto_ptr<StreamWriter>;
#endif
- char* ch = buffer + strlen(buffer) - 1;
- if (*ch != '0') return buffer; // nothing to truncate, so save time
- while(ch > buffer && *ch == '0'){
- --ch;
- }
- char* last_nonzero = ch;
- while(ch >= buffer){
- switch(*ch){
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- --ch;
- continue;
- case '.':
- // Truncate zeroes to save bytes in output, but keep one.
- *(last_nonzero+2) = '\0';
- return buffer;
- default:
- return buffer;
- }
- }
- return buffer;
-}
-
-
-std::string valueToString( bool value )
-{
- return value ? "true" : "false";
-}
-
-std::string valueToQuotedString( const char *value )
-{
- // Not sure how to handle unicode...
- if (strpbrk(value, "\"\\\b\f\n\r\t") == nullptr && !containsControlCharacter( value ))
- return std::string("\"") + value + "\"";
- // We have to walk value and escape any special characters.
- // Appending to std::string is not efficient, but this should be rare.
- // (Note: forward slashes are *not* rare, but I am not escaping them.)
- std::string::size_type maxsize = strlen(value)*2 + 3; // allescaped+quotes+nullptr
- std::string result;
- result.reserve(maxsize); // to avoid lots of mallocs
- result += "\"";
- for (const char* c=value; *c != 0; ++c)
- {
- switch(*c)
- {
- case '\"':
- result += "\\\"";
- break;
- case '\\':
- result += "\\\\";
- break;
- case '\b':
- result += "\\b";
- break;
- case '\f':
- result += "\\f";
- break;
- case '\n':
- result += "\\n";
- break;
- case '\r':
- result += "\\r";
- break;
- case '\t':
- result += "\\t";
- break;
- //case '/':
- // Even though \/ is considered a legal escape in JSON, a bare
- // slash is also legal, so I see no reason to escape it.
- // (I hope I am not misunderstanding something.
- // blep notes: actually escaping \/ may be useful in javascript to avoid </
- // sequence.
- // Should add a flag to allow this compatibility mode and prevent this
- // sequence from occurring.
- default:
- if ( isControlCharacter( *c ) )
- {
- std::ostringstream oss;
- oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c);
- result += oss.str();
- }
- else
- {
- result += *c;
- }
- break;
- }
- }
- result += "\"";
- return result;
-}
-
-// Class Writer
-// //////////////////////////////////////////////////////////////////
-Writer::~Writer()
-{
-}
-
-
-// Class FastWriter
-// //////////////////////////////////////////////////////////////////
-FastWriter::FastWriter()
- : yamlCompatiblityEnabled_( false )
-{
+String valueToString(LargestInt value) {
+ UIntToStringBuffer buffer;
+ char* current = buffer + sizeof(buffer);
+ if (value == Value::minLargestInt) {
+ uintToString(LargestUInt(Value::maxLargestInt) + 1, current);
+ *--current = '-';
+ } else if (value < 0) {
+ uintToString(LargestUInt(-value), current);
+ *--current = '-';
+ } else {
+ uintToString(LargestUInt(value), current);
+ }
+ assert(current >= buffer);
+ return current;
+}
+
+String valueToString(LargestUInt value) {
+ UIntToStringBuffer buffer;
+ char* current = buffer + sizeof(buffer);
+ uintToString(value, current);
+ assert(current >= buffer);
+ return current;
}
+#if defined(JSON_HAS_INT64)
-void
-FastWriter::enableYAMLCompatibility()
-{
- yamlCompatiblityEnabled_ = true;
-}
-
+String valueToString(Int value) { return valueToString(LargestInt(value)); }
-std::string
-FastWriter::write( const Value &root )
-{
- document_ = "";
- writeValue( root );
- document_ += "\n";
- return document_;
-}
+String valueToString(UInt value) { return valueToString(LargestUInt(value)); }
+#endif // # if defined(JSON_HAS_INT64)
-void
-FastWriter::writeValue( const Value &value )
-{
- switch ( value.type() )
- {
- case nullValue:
- document_ += "null";
+namespace {
+String valueToString(double value, bool useSpecialFloats,
+ unsigned int precision, PrecisionType precisionType) {
+ // Print into the buffer. We need not request the alternative representation
+ // that always has a decimal point because JSON doesn't distinguish the
+ // concepts of reals and integers.
+ if (!isfinite(value)) {
+ static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"},
+ {"null", "-1e+9999", "1e+9999"}};
+ return reps[useSpecialFloats ? 0 : 1]
+ [isnan(value) ? 0 : (value < 0) ? 1 : 2];
+ }
+
+ String buffer(size_t(36), '\0');
+ while (true) {
+ int len = jsoncpp_snprintf(
+ &*buffer.begin(), buffer.size(),
+ (precisionType == PrecisionType::significantDigits) ? "%.*g" : "%.*f",
+ precision, value);
+ assert(len >= 0);
+ auto wouldPrint = static_cast<size_t>(len);
+ if (wouldPrint >= buffer.size()) {
+ buffer.resize(wouldPrint + 1);
+ continue;
+ }
+ buffer.resize(wouldPrint);
+ break;
+ }
+
+ buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end());
+
+ // try to ensure we preserve the fact that this was given to us as a double on
+ // input
+ if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) {
+ buffer += ".0";
+ }
+
+ // strip the zero padding from the right
+ if (precisionType == PrecisionType::decimalPlaces) {
+ buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end(), precision),
+ buffer.end());
+ }
+
+ return buffer;
+}
+} // namespace
+
+String valueToString(double value, unsigned int precision,
+ PrecisionType precisionType) {
+ return valueToString(value, false, precision, precisionType);
+}
+
+String valueToString(bool value) { return value ? "true" : "false"; }
+
+static bool doesAnyCharRequireEscaping(char const* s, size_t n) {
+ assert(s || !n);
+
+ return std::any_of(s, s + n, [](unsigned char c) {
+ return c == '\\' || c == '"' || c < 0x20 || c > 0x7F;
+ });
+}
+
+static unsigned int utf8ToCodepoint(const char*& s, const char* e) {
+ const unsigned int REPLACEMENT_CHARACTER = 0xFFFD;
+
+ unsigned int firstByte = static_cast<unsigned char>(*s);
+
+ if (firstByte < 0x80)
+ return firstByte;
+
+ if (firstByte < 0xE0) {
+ if (e - s < 2)
+ return REPLACEMENT_CHARACTER;
+
+ unsigned int calculated =
+ ((firstByte & 0x1F) << 6) | (static_cast<unsigned int>(s[1]) & 0x3F);
+ s += 1;
+ // oversized encoded characters are invalid
+ return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated;
+ }
+
+ if (firstByte < 0xF0) {
+ if (e - s < 3)
+ return REPLACEMENT_CHARACTER;
+
+ unsigned int calculated = ((firstByte & 0x0F) << 12) |
+ ((static_cast<unsigned int>(s[1]) & 0x3F) << 6) |
+ (static_cast<unsigned int>(s[2]) & 0x3F);
+ s += 2;
+ // surrogates aren't valid codepoints itself
+ // shouldn't be UTF-8 encoded
+ if (calculated >= 0xD800 && calculated <= 0xDFFF)
+ return REPLACEMENT_CHARACTER;
+ // oversized encoded characters are invalid
+ return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated;
+ }
+
+ if (firstByte < 0xF8) {
+ if (e - s < 4)
+ return REPLACEMENT_CHARACTER;
+
+ unsigned int calculated = ((firstByte & 0x07) << 18) |
+ ((static_cast<unsigned int>(s[1]) & 0x3F) << 12) |
+ ((static_cast<unsigned int>(s[2]) & 0x3F) << 6) |
+ (static_cast<unsigned int>(s[3]) & 0x3F);
+ s += 3;
+ // oversized encoded characters are invalid
+ return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated;
+ }
+
+ return REPLACEMENT_CHARACTER;
+}
+
+static const char hex2[] = "000102030405060708090a0b0c0d0e0f"
+ "101112131415161718191a1b1c1d1e1f"
+ "202122232425262728292a2b2c2d2e2f"
+ "303132333435363738393a3b3c3d3e3f"
+ "404142434445464748494a4b4c4d4e4f"
+ "505152535455565758595a5b5c5d5e5f"
+ "606162636465666768696a6b6c6d6e6f"
+ "707172737475767778797a7b7c7d7e7f"
+ "808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f"
+ "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
+ "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
+ "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
+ "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
+ "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
+ "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
+
+static String toHex16Bit(unsigned int x) {
+ const unsigned int hi = (x >> 8) & 0xff;
+ const unsigned int lo = x & 0xff;
+ String result(4, ' ');
+ result[0] = hex2[2 * hi];
+ result[1] = hex2[2 * hi + 1];
+ result[2] = hex2[2 * lo];
+ result[3] = hex2[2 * lo + 1];
+ return result;
+}
+
+static void appendRaw(String& result, unsigned ch) {
+ result += static_cast<char>(ch);
+}
+
+static void appendHex(String& result, unsigned ch) {
+ result.append("\\u").append(toHex16Bit(ch));
+}
+
+static String valueToQuotedStringN(const char* value, size_t length,
+ bool emitUTF8 = false) {
+ if (value == nullptr)
+ return "";
+
+ if (!doesAnyCharRequireEscaping(value, length))
+ return String("\"") + value + "\"";
+ // We have to walk value and escape any special characters.
+ // Appending to String is not efficient, but this should be rare.
+ // (Note: forward slashes are *not* rare, but I am not escaping them.)
+ String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL
+ String result;
+ result.reserve(maxsize); // to avoid lots of mallocs
+ result += "\"";
+ char const* end = value + length;
+ for (const char* c = value; c != end; ++c) {
+ switch (*c) {
+ case '\"':
+ result += "\\\"";
break;
- case intValue:
- document_ += valueToString( value.asLargestInt() );
+ case '\\':
+ result += "\\\\";
break;
- case uintValue:
- document_ += valueToString( value.asLargestUInt() );
+ case '\b':
+ result += "\\b";
break;
- case realValue:
- document_ += valueToString( value.asDouble() );
+ case '\f':
+ result += "\\f";
break;
- case stringValue:
- document_ += valueToQuotedString( value.asCString() );
+ case '\n':
+ result += "\\n";
break;
- case booleanValue:
- document_ += valueToString( value.asBool() );
+ case '\r':
+ result += "\\r";
break;
- case arrayValue:
- {
- document_ += "[";
- int size = value.size();
- for ( int index =0; index < size; ++index )
- {
- if ( index > 0 )
- document_ += ",";
- writeValue( value[index] );
- }
- document_ += "]";
- }
+ case '\t':
+ result += "\\t";
break;
- case objectValue:
- {
- Value::Members members( value.getMemberNames() );
- document_ += "{";
- for ( Value::Members::iterator it = members.begin();
- it != members.end();
- ++it )
- {
- const std::string &name = *it;
- if ( it != members.begin() )
- document_ += ",";
- document_ += valueToQuotedString( name.c_str() );
- document_ += yamlCompatiblityEnabled_ ? ": "
- : ":";
- writeValue( value[name] );
- }
- document_ += "}";
+ // case '/':
+ // Even though \/ is considered a legal escape in JSON, a bare
+ // slash is also legal, so I see no reason to escape it.
+ // (I hope I am not misunderstanding something.)
+ // blep notes: actually escaping \/ may be useful in javascript to avoid </
+ // sequence.
+ // Should add a flag to allow this compatibility mode and prevent this
+ // sequence from occurring.
+ default: {
+ if (emitUTF8) {
+ unsigned codepoint = static_cast<unsigned char>(*c);
+ if (codepoint < 0x20) {
+ appendHex(result, codepoint);
+ } else {
+ appendRaw(result, codepoint);
+ }
+ } else {
+ unsigned codepoint = utf8ToCodepoint(c, end); // modifies `c`
+ if (codepoint < 0x20) {
+ appendHex(result, codepoint);
+ } else if (codepoint < 0x80) {
+ appendRaw(result, codepoint);
+ } else if (codepoint < 0x10000) {
+ // Basic Multilingual Plane
+ appendHex(result, codepoint);
+ } else {
+ // Extended Unicode. Encode 20 bits as a surrogate pair.
+ codepoint -= 0x10000;
+ appendHex(result, 0xd800 + ((codepoint >> 10) & 0x3ff));
+ appendHex(result, 0xdc00 + (codepoint & 0x3ff));
+ }
}
- break;
- }
+ } break;
+ }
+ }
+ result += "\"";
+ return result;
+}
+
+String valueToQuotedString(const char* value) {
+ return valueToQuotedStringN(value, strlen(value));
}
+// Class Writer
+// //////////////////////////////////////////////////////////////////
+Writer::~Writer() = default;
-// Class StyledWriter
+// Class FastWriter
// //////////////////////////////////////////////////////////////////
-StyledWriter::StyledWriter()
- : rightMargin_( 74 )
- , indentSize_( 3 )
-{
-}
+FastWriter::FastWriter()
+
+ = default;
+void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; }
-std::string
-StyledWriter::write( const Value &root )
-{
- document_ = "";
- addChildValues_ = false;
- indentString_ = "";
- writeCommentBeforeValue( root );
- writeValue( root );
- writeCommentAfterValueOnSameLine( root );
- document_ += "\n";
- return document_;
+void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
+
+void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
+
+String FastWriter::write(const Value& root) {
+ document_.clear();
+ writeValue(root);
+ if (!omitEndingLineFeed_)
+ document_ += '\n';
+ return document_;
}
+void FastWriter::writeValue(const Value& value) {
+ switch (value.type()) {
+ case nullValue:
+ if (!dropNullPlaceholders_)
+ document_ += "null";
+ break;
+ case intValue:
+ document_ += valueToString(value.asLargestInt());
+ break;
+ case uintValue:
+ document_ += valueToString(value.asLargestUInt());
+ break;
+ case realValue:
+ document_ += valueToString(value.asDouble());
+ break;
+ case stringValue: {
+ // Is NULL possible for value.string_? No.
+ char const* str;
+ char const* end;
+ bool ok = value.getString(&str, &end);
+ if (ok)
+ document_ += valueToQuotedStringN(str, static_cast<size_t>(end - str));
+ break;
+ }
+ case booleanValue:
+ document_ += valueToString(value.asBool());
+ break;
+ case arrayValue: {
+ document_ += '[';
+ ArrayIndex size = value.size();
+ for (ArrayIndex index = 0; index < size; ++index) {
+ if (index > 0)
+ document_ += ',';
+ writeValue(value[index]);
+ }
+ document_ += ']';
+ } break;
+ case objectValue: {
+ Value::Members members(value.getMemberNames());
+ document_ += '{';
+ for (auto it = members.begin(); it != members.end(); ++it) {
+ const String& name = *it;
+ if (it != members.begin())
+ document_ += ',';
+ document_ += valueToQuotedStringN(name.data(), name.length());
+ document_ += yamlCompatibilityEnabled_ ? ": " : ":";
+ writeValue(value[name]);
+ }
+ document_ += '}';
+ } break;
+ }
+}
-void
-StyledWriter::writeValue( const Value &value )
-{
- switch ( value.type() )
- {
- case nullValue:
- pushValue( "null" );
- break;
- case intValue:
- pushValue( valueToString( value.asLargestInt() ) );
- break;
- case uintValue:
- pushValue( valueToString( value.asLargestUInt() ) );
- break;
- case realValue:
- pushValue( valueToString( value.asDouble() ) );
- break;
- case stringValue:
- pushValue( valueToQuotedString( value.asCString() ) );
- break;
- case booleanValue:
- pushValue( valueToString( value.asBool() ) );
- break;
- case arrayValue:
- writeArrayValue( value);
- break;
- case objectValue:
- {
- Value::Members members( value.getMemberNames() );
- if ( members.empty() )
- pushValue( "{}" );
- else
- {
- writeWithIndent( "{" );
- indent();
- Value::Members::iterator it = members.begin();
- for (;;)
- {
- const std::string &name = *it;
- const Value &childValue = value[name];
- writeCommentBeforeValue( childValue );
- writeWithIndent( valueToQuotedString( name.c_str() ) );
- document_ += " : ";
- writeValue( childValue );
- if ( ++it == members.end() )
- {
- writeCommentAfterValueOnSameLine( childValue );
- break;
- }
- document_ += ",";
- writeCommentAfterValueOnSameLine( childValue );
- }
- unindent();
- writeWithIndent( "}" );
- }
+// Class StyledWriter
+// //////////////////////////////////////////////////////////////////
+
+StyledWriter::StyledWriter() = default;
+
+String StyledWriter::write(const Value& root) {
+ document_.clear();
+ addChildValues_ = false;
+ indentString_.clear();
+ writeCommentBeforeValue(root);
+ writeValue(root);
+ writeCommentAfterValueOnSameLine(root);
+ document_ += '\n';
+ return document_;
+}
+
+void StyledWriter::writeValue(const Value& value) {
+ switch (value.type()) {
+ case nullValue:
+ pushValue("null");
+ break;
+ case intValue:
+ pushValue(valueToString(value.asLargestInt()));
+ break;
+ case uintValue:
+ pushValue(valueToString(value.asLargestUInt()));
+ break;
+ case realValue:
+ pushValue(valueToString(value.asDouble()));
+ break;
+ case stringValue: {
+ // Is NULL possible for value.string_? No.
+ char const* str;
+ char const* end;
+ bool ok = value.getString(&str, &end);
+ if (ok)
+ pushValue(valueToQuotedStringN(str, static_cast<size_t>(end - str)));
+ else
+ pushValue("");
+ break;
+ }
+ case booleanValue:
+ pushValue(valueToString(value.asBool()));
+ break;
+ case arrayValue:
+ writeArrayValue(value);
+ break;
+ case objectValue: {
+ Value::Members members(value.getMemberNames());
+ if (members.empty())
+ pushValue("{}");
+ else {
+ writeWithIndent("{");
+ indent();
+ auto it = members.begin();
+ for (;;) {
+ const String& name = *it;
+ const Value& childValue = value[name];
+ writeCommentBeforeValue(childValue);
+ writeWithIndent(valueToQuotedString(name.c_str()));
+ document_ += " : ";
+ writeValue(childValue);
+ if (++it == members.end()) {
+ writeCommentAfterValueOnSameLine(childValue);
+ break;
+ }
+ document_ += ',';
+ writeCommentAfterValueOnSameLine(childValue);
}
- break;
- }
-}
-
-
-void
-StyledWriter::writeArrayValue( const Value &value )
-{
- unsigned size = value.size();
- if ( size == 0 )
- pushValue( "[]" );
- else
- {
- bool isArrayMultiLine = isMultineArray( value );
- if ( isArrayMultiLine )
- {
- writeWithIndent( "[" );
- indent();
- bool hasChildValue = !childValues_.empty();
- unsigned index =0;
- for (;;)
- {
- const Value &childValue = value[index];
- writeCommentBeforeValue( childValue );
- if ( hasChildValue )
- writeWithIndent( childValues_[index] );
- else
- {
- writeIndent();
- writeValue( childValue );
- }
- if ( ++index == size )
- {
- writeCommentAfterValueOnSameLine( childValue );
- break;
- }
- document_ += ",";
- writeCommentAfterValueOnSameLine( childValue );
- }
- unindent();
- writeWithIndent( "]" );
+ unindent();
+ writeWithIndent("}");
+ }
+ } break;
+ }
+}
+
+void StyledWriter::writeArrayValue(const Value& value) {
+ size_t size = value.size();
+ if (size == 0)
+ pushValue("[]");
+ else {
+ bool isArrayMultiLine = isMultilineArray(value);
+ if (isArrayMultiLine) {
+ writeWithIndent("[");
+ indent();
+ bool hasChildValue = !childValues_.empty();
+ ArrayIndex index = 0;
+ for (;;) {
+ const Value& childValue = value[index];
+ writeCommentBeforeValue(childValue);
+ if (hasChildValue)
+ writeWithIndent(childValues_[index]);
+ else {
+ writeIndent();
+ writeValue(childValue);
+ }
+ if (++index == size) {
+ writeCommentAfterValueOnSameLine(childValue);
+ break;
+ }
+ document_ += ',';
+ writeCommentAfterValueOnSameLine(childValue);
}
- else // output on a single line
- {
- assert( childValues_.size() == size );
- document_ += "[ ";
- for ( unsigned index =0; index < size; ++index )
- {
- if ( index > 0 )
- document_ += ", ";
- document_ += childValues_[index];
- }
- document_ += " ]";
+ unindent();
+ writeWithIndent("]");
+ } else // output on a single line
+ {
+ assert(childValues_.size() == size);
+ document_ += "[ ";
+ for (size_t index = 0; index < size; ++index) {
+ if (index > 0)
+ document_ += ", ";
+ document_ += childValues_[index];
}
- }
-}
-
-
-bool
-StyledWriter::isMultineArray( const Value &value )
-{
- int size = value.size();
- bool isMultiLine = size*3 >= rightMargin_ ;
- childValues_.clear();
- for ( int index =0; index < size && !isMultiLine; ++index )
- {
- const Value &childValue = value[index];
- isMultiLine = isMultiLine ||
- ( (childValue.isArray() || childValue.isObject()) &&
- childValue.size() > 0 );
- }
- if ( !isMultiLine ) // check if line length > max line length
- {
- childValues_.reserve( size );
- addChildValues_ = true;
- int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
- for ( int index =0; index < size && !isMultiLine; ++index )
- {
- writeValue( value[index] );
- lineLength += int( childValues_[index].length() );
- isMultiLine = isMultiLine && hasCommentForValue( value[index] );
+ document_ += " ]";
+ }
+ }
+}
+
+bool StyledWriter::isMultilineArray(const Value& value) {
+ ArrayIndex const size = value.size();
+ bool isMultiLine = size * 3 >= rightMargin_;
+ childValues_.clear();
+ for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
+ const Value& childValue = value[index];
+ isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
+ !childValue.empty());
+ }
+ if (!isMultiLine) // check if line length > max line length
+ {
+ childValues_.reserve(size);
+ addChildValues_ = true;
+ ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
+ for (ArrayIndex index = 0; index < size; ++index) {
+ if (hasCommentForValue(value[index])) {
+ isMultiLine = true;
}
- addChildValues_ = false;
- isMultiLine = isMultiLine || lineLength >= rightMargin_;
- }
- return isMultiLine;
+ writeValue(value[index]);
+ lineLength += static_cast<ArrayIndex>(childValues_[index].length());
+ }
+ addChildValues_ = false;
+ isMultiLine = isMultiLine || lineLength >= rightMargin_;
+ }
+ return isMultiLine;
+}
+
+void StyledWriter::pushValue(const String& value) {
+ if (addChildValues_)
+ childValues_.push_back(value);
+ else
+ document_ += value;
+}
+
+void StyledWriter::writeIndent() {
+ if (!document_.empty()) {
+ char last = document_[document_.length() - 1];
+ if (last == ' ') // already indented
+ return;
+ if (last != '\n') // Comments may add new-line
+ document_ += '\n';
+ }
+ document_ += indentString_;
}
-
-void
-StyledWriter::pushValue( const std::string &value )
-{
- if ( addChildValues_ )
- childValues_.push_back( value );
- else
- document_ += value;
+void StyledWriter::writeWithIndent(const String& value) {
+ writeIndent();
+ document_ += value;
}
+void StyledWriter::indent() { indentString_ += String(indentSize_, ' '); }
-void
-StyledWriter::writeIndent()
-{
- if ( !document_.empty() )
- {
- char last = document_[document_.length()-1];
- if ( last == ' ' ) // already indented
- return;
- if ( last != '\n' ) // Comments may add new-line
- document_ += '\n';
- }
- document_ += indentString_;
+void StyledWriter::unindent() {
+ assert(indentString_.size() >= indentSize_);
+ indentString_.resize(indentString_.size() - indentSize_);
}
+void StyledWriter::writeCommentBeforeValue(const Value& root) {
+ if (!root.hasComment(commentBefore))
+ return;
-void
-StyledWriter::writeWithIndent( const std::string &value )
-{
- writeIndent();
- document_ += value;
-}
-
+ document_ += '\n';
+ writeIndent();
+ const String& comment = root.getComment(commentBefore);
+ String::const_iterator iter = comment.begin();
+ while (iter != comment.end()) {
+ document_ += *iter;
+ if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
+ writeIndent();
+ ++iter;
+ }
-void
-StyledWriter::indent()
-{
- indentString_ += std::string( indentSize_, ' ' );
+ // Comments are stripped of trailing newlines, so add one here
+ document_ += '\n';
}
+void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
+ if (root.hasComment(commentAfterOnSameLine))
+ document_ += " " + root.getComment(commentAfterOnSameLine);
-void
-StyledWriter::unindent()
-{
- assert( int(indentString_.size()) >= indentSize_ );
- indentString_.resize( indentString_.size() - indentSize_ );
+ if (root.hasComment(commentAfter)) {
+ document_ += '\n';
+ document_ += root.getComment(commentAfter);
+ document_ += '\n';
+ }
}
-
-void
-StyledWriter::writeCommentBeforeValue( const Value &root )
-{
- if ( !root.hasComment( commentBefore ) )
- return;
- document_ += normalizeEOL( root.getComment( commentBefore ) );
- document_ += "\n";
-}
-
-
-void
-StyledWriter::writeCommentAfterValueOnSameLine( const Value &root )
-{
- if ( root.hasComment( commentAfterOnSameLine ) )
- document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
-
- if ( root.hasComment( commentAfter ) )
- {
- document_ += "\n";
- document_ += normalizeEOL( root.getComment( commentAfter ) );
- document_ += "\n";
- }
-}
-
-
-bool
-StyledWriter::hasCommentForValue( const Value &value )
-{
- return value.hasComment( commentBefore )
- || value.hasComment( commentAfterOnSameLine )
- || value.hasComment( commentAfter );
-}
-
-
-std::string
-StyledWriter::normalizeEOL( const std::string &text )
-{
- std::string normalized;
- normalized.reserve( text.length() );
- const char *begin = text.c_str();
- const char *end = begin + text.length();
- const char *current = begin;
- while ( current != end )
- {
- char c = *current++;
- if ( c == '\r' ) // mac or dos EOL
- {
- if ( *current == '\n' ) // convert dos EOL
- ++current;
- normalized += '\n';
- }
- else // handle unix EOL & other char
- normalized += c;
- }
- return normalized;
+bool StyledWriter::hasCommentForValue(const Value& value) {
+ return value.hasComment(commentBefore) ||
+ value.hasComment(commentAfterOnSameLine) ||
+ value.hasComment(commentAfter);
}
-
// Class StyledStreamWriter
// //////////////////////////////////////////////////////////////////
-StyledStreamWriter::StyledStreamWriter( std::string indentation )
- : document_(nullptr)
- , rightMargin_( 74 )
- , indentation_( indentation )
-{
-}
-
-
-void
-StyledStreamWriter::write( std::ostream &out, const Value &root )
-{
- document_ = &out;
- addChildValues_ = false;
- indentString_ = "";
- writeCommentBeforeValue( root );
- writeValue( root );
- writeCommentAfterValueOnSameLine( root );
- *document_ << "\n";
- document_ = nullptr; // Forget the stream, for safety.
-}
-
-
-void
-StyledStreamWriter::writeValue( const Value &value )
-{
- switch ( value.type() )
- {
- case nullValue:
- pushValue( "null" );
- break;
- case intValue:
- pushValue( valueToString( value.asLargestInt() ) );
- break;
- case uintValue:
- pushValue( valueToString( value.asLargestUInt() ) );
- break;
- case realValue:
- pushValue( valueToString( value.asDouble() ) );
- break;
- case stringValue:
- pushValue( valueToQuotedString( value.asCString() ) );
- break;
- case booleanValue:
- pushValue( valueToString( value.asBool() ) );
- break;
- case arrayValue:
- writeArrayValue( value);
- break;
- case objectValue:
- {
- Value::Members members( value.getMemberNames() );
- if ( members.empty() )
- pushValue( "{}" );
- else
- {
- writeWithIndent( "{" );
- indent();
- Value::Members::iterator it = members.begin();
- for (;;)
- {
- const std::string &name = *it;
- const Value &childValue = value[name];
- writeCommentBeforeValue( childValue );
- writeWithIndent( valueToQuotedString( name.c_str() ) );
- *document_ << " : ";
- writeValue( childValue );
- if ( ++it == members.end() )
- {
- writeCommentAfterValueOnSameLine( childValue );
- break;
- }
- *document_ << ",";
- writeCommentAfterValueOnSameLine( childValue );
- }
- unindent();
- writeWithIndent( "}" );
- }
+StyledStreamWriter::StyledStreamWriter(String indentation)
+ : document_(nullptr), indentation_(std::move(indentation)),
+ addChildValues_(), indented_(false) {}
+
+void StyledStreamWriter::write(OStream& out, const Value& root) {
+ document_ = &out;
+ addChildValues_ = false;
+ indentString_.clear();
+ indented_ = true;
+ writeCommentBeforeValue(root);
+ if (!indented_)
+ writeIndent();
+ indented_ = true;
+ writeValue(root);
+ writeCommentAfterValueOnSameLine(root);
+ *document_ << "\n";
+ document_ = nullptr; // Forget the stream, for safety.
+}
+
+void StyledStreamWriter::writeValue(const Value& value) {
+ switch (value.type()) {
+ case nullValue:
+ pushValue("null");
+ break;
+ case intValue:
+ pushValue(valueToString(value.asLargestInt()));
+ break;
+ case uintValue:
+ pushValue(valueToString(value.asLargestUInt()));
+ break;
+ case realValue:
+ pushValue(valueToString(value.asDouble()));
+ break;
+ case stringValue: {
+ // Is NULL possible for value.string_? No.
+ char const* str;
+ char const* end;
+ bool ok = value.getString(&str, &end);
+ if (ok)
+ pushValue(valueToQuotedStringN(str, static_cast<size_t>(end - str)));
+ else
+ pushValue("");
+ break;
+ }
+ case booleanValue:
+ pushValue(valueToString(value.asBool()));
+ break;
+ case arrayValue:
+ writeArrayValue(value);
+ break;
+ case objectValue: {
+ Value::Members members(value.getMemberNames());
+ if (members.empty())
+ pushValue("{}");
+ else {
+ writeWithIndent("{");
+ indent();
+ auto it = members.begin();
+ for (;;) {
+ const String& name = *it;
+ const Value& childValue = value[name];
+ writeCommentBeforeValue(childValue);
+ writeWithIndent(valueToQuotedString(name.c_str()));
+ *document_ << " : ";
+ writeValue(childValue);
+ if (++it == members.end()) {
+ writeCommentAfterValueOnSameLine(childValue);
+ break;
+ }
+ *document_ << ",";
+ writeCommentAfterValueOnSameLine(childValue);
}
- break;
- }
-}
-
-
-void
-StyledStreamWriter::writeArrayValue( const Value &value )
-{
- unsigned size = value.size();
- if ( size == 0 )
- pushValue( "[]" );
- else
- {
- bool isArrayMultiLine = isMultineArray( value );
- if ( isArrayMultiLine )
- {
- writeWithIndent( "[" );
- indent();
- bool hasChildValue = !childValues_.empty();
- unsigned index =0;
- for (;;)
- {
- const Value &childValue = value[index];
- writeCommentBeforeValue( childValue );
- if ( hasChildValue )
- writeWithIndent( childValues_[index] );
- else
- {
- writeIndent();
- writeValue( childValue );
- }
- if ( ++index == size )
- {
- writeCommentAfterValueOnSameLine( childValue );
- break;
- }
- *document_ << ",";
- writeCommentAfterValueOnSameLine( childValue );
- }
- unindent();
- writeWithIndent( "]" );
+ unindent();
+ writeWithIndent("}");
+ }
+ } break;
+ }
+}
+
+void StyledStreamWriter::writeArrayValue(const Value& value) {
+ unsigned size = value.size();
+ if (size == 0)
+ pushValue("[]");
+ else {
+ bool isArrayMultiLine = isMultilineArray(value);
+ if (isArrayMultiLine) {
+ writeWithIndent("[");
+ indent();
+ bool hasChildValue = !childValues_.empty();
+ unsigned index = 0;
+ for (;;) {
+ const Value& childValue = value[index];
+ writeCommentBeforeValue(childValue);
+ if (hasChildValue)
+ writeWithIndent(childValues_[index]);
+ else {
+ if (!indented_)
+ writeIndent();
+ indented_ = true;
+ writeValue(childValue);
+ indented_ = false;
+ }
+ if (++index == size) {
+ writeCommentAfterValueOnSameLine(childValue);
+ break;
+ }
+ *document_ << ",";
+ writeCommentAfterValueOnSameLine(childValue);
}
- else // output on a single line
- {
- assert( childValues_.size() == size );
- *document_ << "[ ";
- for ( unsigned index =0; index < size; ++index )
- {
- if ( index > 0 )
- *document_ << ", ";
- *document_ << childValues_[index];
- }
- *document_ << " ]";
+ unindent();
+ writeWithIndent("]");
+ } else // output on a single line
+ {
+ assert(childValues_.size() == size);
+ *document_ << "[ ";
+ for (unsigned index = 0; index < size; ++index) {
+ if (index > 0)
+ *document_ << ", ";
+ *document_ << childValues_[index];
}
- }
-}
-
-
-bool
-StyledStreamWriter::isMultineArray( const Value &value )
-{
- int size = value.size();
- bool isMultiLine = size*3 >= rightMargin_ ;
- childValues_.clear();
- for ( int index =0; index < size && !isMultiLine; ++index )
- {
- const Value &childValue = value[index];
- isMultiLine = isMultiLine ||
- ( (childValue.isArray() || childValue.isObject()) &&
- childValue.size() > 0 );
- }
- if ( !isMultiLine ) // check if line length > max line length
- {
- childValues_.reserve( size );
- addChildValues_ = true;
- int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
- for ( int index =0; index < size && !isMultiLine; ++index )
- {
- writeValue( value[index] );
- lineLength += int( childValues_[index].length() );
- isMultiLine = isMultiLine && hasCommentForValue( value[index] );
+ *document_ << " ]";
+ }
+ }
+}
+
+bool StyledStreamWriter::isMultilineArray(const Value& value) {
+ ArrayIndex const size = value.size();
+ bool isMultiLine = size * 3 >= rightMargin_;
+ childValues_.clear();
+ for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
+ const Value& childValue = value[index];
+ isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
+ !childValue.empty());
+ }
+ if (!isMultiLine) // check if line length > max line length
+ {
+ childValues_.reserve(size);
+ addChildValues_ = true;
+ ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
+ for (ArrayIndex index = 0; index < size; ++index) {
+ if (hasCommentForValue(value[index])) {
+ isMultiLine = true;
}
- addChildValues_ = false;
- isMultiLine = isMultiLine || lineLength >= rightMargin_;
- }
- return isMultiLine;
-}
+ writeValue(value[index]);
+ lineLength += static_cast<ArrayIndex>(childValues_[index].length());
+ }
+ addChildValues_ = false;
+ isMultiLine = isMultiLine || lineLength >= rightMargin_;
+ }
+ return isMultiLine;
+}
+
+void StyledStreamWriter::pushValue(const String& value) {
+ if (addChildValues_)
+ childValues_.push_back(value);
+ else
+ *document_ << value;
+}
+
+void StyledStreamWriter::writeIndent() {
+ // blep intended this to look at the so-far-written string
+ // to determine whether we are already indented, but
+ // with a stream we cannot do that. So we rely on some saved state.
+ // The caller checks indented_.
+ *document_ << '\n' << indentString_;
+}
+
+void StyledStreamWriter::writeWithIndent(const String& value) {
+ if (!indented_)
+ writeIndent();
+ *document_ << value;
+ indented_ = false;
+}
+
+void StyledStreamWriter::indent() { indentString_ += indentation_; }
+
+void StyledStreamWriter::unindent() {
+ assert(indentString_.size() >= indentation_.size());
+ indentString_.resize(indentString_.size() - indentation_.size());
+}
+
+void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
+ if (!root.hasComment(commentBefore))
+ return;
+
+ if (!indented_)
+ writeIndent();
+ const String& comment = root.getComment(commentBefore);
+ String::const_iterator iter = comment.begin();
+ while (iter != comment.end()) {
+ *document_ << *iter;
+ if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
+ // writeIndent(); // would include newline
+ *document_ << indentString_;
+ ++iter;
+ }
+ indented_ = false;
+}
+
+void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
+ if (root.hasComment(commentAfterOnSameLine))
+ *document_ << ' ' << root.getComment(commentAfterOnSameLine);
+
+ if (root.hasComment(commentAfter)) {
+ writeIndent();
+ *document_ << root.getComment(commentAfter);
+ }
+ indented_ = false;
+}
+
+bool StyledStreamWriter::hasCommentForValue(const Value& value) {
+ return value.hasComment(commentBefore) ||
+ value.hasComment(commentAfterOnSameLine) ||
+ value.hasComment(commentAfter);
+}
+
+//////////////////////////
+// BuiltStyledStreamWriter
+
+/// Scoped enums are not available until C++11.
+struct CommentStyle {
+ /// Decide whether to write comments.
+ enum Enum {
+ None, ///< Drop all comments.
+ Most, ///< Recover odd behavior of previous versions (not implemented yet).
+ All ///< Keep all comments.
+ };
+};
+struct BuiltStyledStreamWriter : public StreamWriter {
+ BuiltStyledStreamWriter(String indentation, CommentStyle::Enum cs,
+ String colonSymbol, String nullSymbol,
+ String endingLineFeedSymbol, bool useSpecialFloats,
+ bool emitUTF8, unsigned int precision,
+ PrecisionType precisionType);
+ int write(Value const& root, OStream* sout) override;
-void
-StyledStreamWriter::pushValue( const std::string &value )
-{
- if ( addChildValues_ )
- childValues_.push_back( value );
- else
- *document_ << value;
+private:
+ void writeValue(Value const& value);
+ void writeArrayValue(Value const& value);
+ bool isMultilineArray(Value const& value);
+ void pushValue(String const& value);
+ void writeIndent();
+ void writeWithIndent(String const& value);
+ void indent();
+ void unindent();
+ void writeCommentBeforeValue(Value const& root);
+ void writeCommentAfterValueOnSameLine(Value const& root);
+ static bool hasCommentForValue(const Value& value);
+
+ using ChildValues = std::vector<String>;
+
+ ChildValues childValues_;
+ String indentString_;
+ unsigned int rightMargin_;
+ String indentation_;
+ CommentStyle::Enum cs_;
+ String colonSymbol_;
+ String nullSymbol_;
+ String endingLineFeedSymbol_;
+ bool addChildValues_ : 1;
+ bool indented_ : 1;
+ bool useSpecialFloats_ : 1;
+ bool emitUTF8_ : 1;
+ unsigned int precision_;
+ PrecisionType precisionType_;
+};
+BuiltStyledStreamWriter::BuiltStyledStreamWriter(
+ String indentation, CommentStyle::Enum cs, String colonSymbol,
+ String nullSymbol, String endingLineFeedSymbol, bool useSpecialFloats,
+ bool emitUTF8, unsigned int precision, PrecisionType precisionType)
+ : rightMargin_(74), indentation_(std::move(indentation)), cs_(cs),
+ colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)),
+ endingLineFeedSymbol_(std::move(endingLineFeedSymbol)),
+ addChildValues_(false), indented_(false),
+ useSpecialFloats_(useSpecialFloats), emitUTF8_(emitUTF8),
+ precision_(precision), precisionType_(precisionType) {}
+int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) {
+ sout_ = sout;
+ addChildValues_ = false;
+ indented_ = true;
+ indentString_.clear();
+ writeCommentBeforeValue(root);
+ if (!indented_)
+ writeIndent();
+ indented_ = true;
+ writeValue(root);
+ writeCommentAfterValueOnSameLine(root);
+ *sout_ << endingLineFeedSymbol_;
+ sout_ = nullptr;
+ return 0;
+}
+void BuiltStyledStreamWriter::writeValue(Value const& value) {
+ switch (value.type()) {
+ case nullValue:
+ pushValue(nullSymbol_);
+ break;
+ case intValue:
+ pushValue(valueToString(value.asLargestInt()));
+ break;
+ case uintValue:
+ pushValue(valueToString(value.asLargestUInt()));
+ break;
+ case realValue:
+ pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_,
+ precisionType_));
+ break;
+ case stringValue: {
+ // Is NULL is possible for value.string_? No.
+ char const* str;
+ char const* end;
+ bool ok = value.getString(&str, &end);
+ if (ok)
+ pushValue(
+ valueToQuotedStringN(str, static_cast<size_t>(end - str), emitUTF8_));
+ else
+ pushValue("");
+ break;
+ }
+ case booleanValue:
+ pushValue(valueToString(value.asBool()));
+ break;
+ case arrayValue:
+ writeArrayValue(value);
+ break;
+ case objectValue: {
+ Value::Members members(value.getMemberNames());
+ if (members.empty())
+ pushValue("{}");
+ else {
+ writeWithIndent("{");
+ indent();
+ auto it = members.begin();
+ for (;;) {
+ String const& name = *it;
+ Value const& childValue = value[name];
+ writeCommentBeforeValue(childValue);
+ writeWithIndent(
+ valueToQuotedStringN(name.data(), name.length(), emitUTF8_));
+ *sout_ << colonSymbol_;
+ writeValue(childValue);
+ if (++it == members.end()) {
+ writeCommentAfterValueOnSameLine(childValue);
+ break;
+ }
+ *sout_ << ",";
+ writeCommentAfterValueOnSameLine(childValue);
+ }
+ unindent();
+ writeWithIndent("}");
+ }
+ } break;
+ }
+}
+
+void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
+ unsigned size = value.size();
+ if (size == 0)
+ pushValue("[]");
+ else {
+ bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value);
+ if (isMultiLine) {
+ writeWithIndent("[");
+ indent();
+ bool hasChildValue = !childValues_.empty();
+ unsigned index = 0;
+ for (;;) {
+ Value const& childValue = value[index];
+ writeCommentBeforeValue(childValue);
+ if (hasChildValue)
+ writeWithIndent(childValues_[index]);
+ else {
+ if (!indented_)
+ writeIndent();
+ indented_ = true;
+ writeValue(childValue);
+ indented_ = false;
+ }
+ if (++index == size) {
+ writeCommentAfterValueOnSameLine(childValue);
+ break;
+ }
+ *sout_ << ",";
+ writeCommentAfterValueOnSameLine(childValue);
+ }
+ unindent();
+ writeWithIndent("]");
+ } else // output on a single line
+ {
+ assert(childValues_.size() == size);
+ *sout_ << "[";
+ if (!indentation_.empty())
+ *sout_ << " ";
+ for (unsigned index = 0; index < size; ++index) {
+ if (index > 0)
+ *sout_ << ((!indentation_.empty()) ? ", " : ",");
+ *sout_ << childValues_[index];
+ }
+ if (!indentation_.empty())
+ *sout_ << " ";
+ *sout_ << "]";
+ }
+ }
+}
+
+bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) {
+ ArrayIndex const size = value.size();
+ bool isMultiLine = size * 3 >= rightMargin_;
+ childValues_.clear();
+ for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
+ Value const& childValue = value[index];
+ isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
+ !childValue.empty());
+ }
+ if (!isMultiLine) // check if line length > max line length
+ {
+ childValues_.reserve(size);
+ addChildValues_ = true;
+ ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
+ for (ArrayIndex index = 0; index < size; ++index) {
+ if (hasCommentForValue(value[index])) {
+ isMultiLine = true;
+ }
+ writeValue(value[index]);
+ lineLength += static_cast<ArrayIndex>(childValues_[index].length());
+ }
+ addChildValues_ = false;
+ isMultiLine = isMultiLine || lineLength >= rightMargin_;
+ }
+ return isMultiLine;
+}
+
+void BuiltStyledStreamWriter::pushValue(String const& value) {
+ if (addChildValues_)
+ childValues_.push_back(value);
+ else
+ *sout_ << value;
+}
+
+void BuiltStyledStreamWriter::writeIndent() {
+ // blep intended this to look at the so-far-written string
+ // to determine whether we are already indented, but
+ // with a stream we cannot do that. So we rely on some saved state.
+ // The caller checks indented_.
+
+ if (!indentation_.empty()) {
+ // In this case, drop newlines too.
+ *sout_ << '\n' << indentString_;
+ }
+}
+
+void BuiltStyledStreamWriter::writeWithIndent(String const& value) {
+ if (!indented_)
+ writeIndent();
+ *sout_ << value;
+ indented_ = false;
+}
+
+void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
+
+void BuiltStyledStreamWriter::unindent() {
+ assert(indentString_.size() >= indentation_.size());
+ indentString_.resize(indentString_.size() - indentation_.size());
+}
+
+void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
+ if (cs_ == CommentStyle::None)
+ return;
+ if (!root.hasComment(commentBefore))
+ return;
+
+ if (!indented_)
+ writeIndent();
+ const String& comment = root.getComment(commentBefore);
+ String::const_iterator iter = comment.begin();
+ while (iter != comment.end()) {
+ *sout_ << *iter;
+ if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
+ // writeIndent(); // would write extra newline
+ *sout_ << indentString_;
+ ++iter;
+ }
+ indented_ = false;
+}
+
+void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(
+ Value const& root) {
+ if (cs_ == CommentStyle::None)
+ return;
+ if (root.hasComment(commentAfterOnSameLine))
+ *sout_ << " " + root.getComment(commentAfterOnSameLine);
+
+ if (root.hasComment(commentAfter)) {
+ writeIndent();
+ *sout_ << root.getComment(commentAfter);
+ }
+}
+
+// static
+bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
+ return value.hasComment(commentBefore) ||
+ value.hasComment(commentAfterOnSameLine) ||
+ value.hasComment(commentAfter);
+}
+
+///////////////
+// StreamWriter
+
+StreamWriter::StreamWriter() : sout_(nullptr) {}
+StreamWriter::~StreamWriter() = default;
+StreamWriter::Factory::~Factory() = default;
+StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); }
+StreamWriterBuilder::~StreamWriterBuilder() = default;
+StreamWriter* StreamWriterBuilder::newStreamWriter() const {
+ const String indentation = settings_["indentation"].asString();
+ const String cs_str = settings_["commentStyle"].asString();
+ const String pt_str = settings_["precisionType"].asString();
+ const bool eyc = settings_["enableYAMLCompatibility"].asBool();
+ const bool dnp = settings_["dropNullPlaceholders"].asBool();
+ const bool usf = settings_["useSpecialFloats"].asBool();
+ const bool emitUTF8 = settings_["emitUTF8"].asBool();
+ unsigned int pre = settings_["precision"].asUInt();
+ CommentStyle::Enum cs = CommentStyle::All;
+ if (cs_str == "All") {
+ cs = CommentStyle::All;
+ } else if (cs_str == "None") {
+ cs = CommentStyle::None;
+ } else {
+ throwRuntimeError("commentStyle must be 'All' or 'None'");
+ }
+ PrecisionType precisionType(significantDigits);
+ if (pt_str == "significant") {
+ precisionType = PrecisionType::significantDigits;
+ } else if (pt_str == "decimal") {
+ precisionType = PrecisionType::decimalPlaces;
+ } else {
+ throwRuntimeError("precisionType must be 'significant' or 'decimal'");
+ }
+ String colonSymbol = " : ";
+ if (eyc) {
+ colonSymbol = ": ";
+ } else if (indentation.empty()) {
+ colonSymbol = ":";
+ }
+ String nullSymbol = "null";
+ if (dnp) {
+ nullSymbol.clear();
+ }
+ if (pre > 17)
+ pre = 17;
+ String endingLineFeedSymbol;
+ return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol,
+ endingLineFeedSymbol, usf, emitUTF8, pre,
+ precisionType);
+}
+
+bool StreamWriterBuilder::validate(Json::Value* invalid) const {
+ static const auto& valid_keys = *new std::set<String>{
+ "indentation",
+ "commentStyle",
+ "enableYAMLCompatibility",
+ "dropNullPlaceholders",
+ "useSpecialFloats",
+ "emitUTF8",
+ "precision",
+ "precisionType",
+ };
+ for (auto si = settings_.begin(); si != settings_.end(); ++si) {
+ auto key = si.name();
+ if (valid_keys.count(key))
+ continue;
+ if (invalid)
+ (*invalid)[key] = *si;
+ else
+ return false;
+ }
+ return invalid ? invalid->empty() : true;
}
-
-void
-StyledStreamWriter::writeIndent()
-{
- /*
- Some comments in this method would have been nice. ;-)
-
- if ( !document_.empty() )
- {
- char last = document_[document_.length()-1];
- if ( last == ' ' ) // already indented
- return;
- if ( last != '\n' ) // Comments may add new-line
- *document_ << '\n';
- }
- */
- *document_ << '\n' << indentString_;
+Value& StreamWriterBuilder::operator[](const String& key) {
+ return settings_[key];
}
-
-
-void
-StyledStreamWriter::writeWithIndent( const std::string &value )
-{
- writeIndent();
- *document_ << value;
+// static
+void StreamWriterBuilder::setDefaults(Json::Value* settings) {
+ //! [StreamWriterBuilderDefaults]
+ (*settings)["commentStyle"] = "All";
+ (*settings)["indentation"] = "\t";
+ (*settings)["enableYAMLCompatibility"] = false;
+ (*settings)["dropNullPlaceholders"] = false;
+ (*settings)["useSpecialFloats"] = false;
+ (*settings)["emitUTF8"] = false;
+ (*settings)["precision"] = 17;
+ (*settings)["precisionType"] = "significant";
+ //! [StreamWriterBuilderDefaults]
}
-
-void
-StyledStreamWriter::indent()
-{
- indentString_ += indentation_;
+String writeString(StreamWriter::Factory const& factory, Value const& root) {
+ OStringStream sout;
+ StreamWriterPtr const writer(factory.newStreamWriter());
+ writer->write(root, &sout);
+ return sout.str();
}
-
-void
-StyledStreamWriter::unindent()
-{
- assert( indentString_.size() >= indentation_.size() );
- indentString_.resize( indentString_.size() - indentation_.size() );
+OStream& operator<<(OStream& sout, Value const& root) {
+ StreamWriterBuilder builder;
+ StreamWriterPtr const writer(builder.newStreamWriter());
+ writer->write(root, &sout);
+ return sout;
}
+} // namespace Json
-void
-StyledStreamWriter::writeCommentBeforeValue( const Value &root )
-{
- if ( !root.hasComment( commentBefore ) )
- return;
- *document_ << normalizeEOL( root.getComment( commentBefore ) );
- *document_ << "\n";
-}
-
-
-void
-StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root )
-{
- if ( root.hasComment( commentAfterOnSameLine ) )
- *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
-
- if ( root.hasComment( commentAfter ) )
- {
- *document_ << "\n";
- *document_ << normalizeEOL( root.getComment( commentAfter ) );
- *document_ << "\n";
- }
-}
-
-
-bool
-StyledStreamWriter::hasCommentForValue( const Value &value )
-{
- return value.hasComment( commentBefore )
- || value.hasComment( commentAfterOnSameLine )
- || value.hasComment( commentAfter );
-}
-
-
-std::string
-StyledStreamWriter::normalizeEOL( const std::string &text )
-{
- std::string normalized;
- normalized.reserve( text.length() );
- const char *begin = text.c_str();
- const char *end = begin + text.length();
- const char *current = begin;
- while ( current != end )
- {
- char c = *current++;
- if ( c == '\r' ) // mac or dos EOL
- {
- if ( *current == '\n' ) // convert dos EOL
- ++current;
- normalized += '\n';
- }
- else // handle unix EOL & other char
- normalized += c;
- }
- return normalized;
-}
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_writer.cpp
+// //////////////////////////////////////////////////////////////////////
-std::ostream& operator<<( std::ostream &sout, const Value &root )
-{
- Json::StyledStreamWriter writer;
- writer.write(sout, root);
- return sout;
-}
-} // namespace Json
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: src/lib_json/json_writer.cpp
-// //////////////////////////////////////////////////////////////////////
diff --git a/Modules/CppMicroServices/third_party/jsoncpp.h b/Modules/CppMicroServices/third_party/jsoncpp.h
index f8a746efc5..b280790a4e 100644
--- a/Modules/CppMicroServices/third_party/jsoncpp.h
+++ b/Modules/CppMicroServices/third_party/jsoncpp.h
@@ -1,1855 +1,2346 @@
-/// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/).
-/// It is intented to be used with #include <json/json.h>
+/// Json-cpp amalgamated header (http://jsoncpp.sourceforge.net/).
+/// It is intended to be used with #include "json/json.h"
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: LICENSE
// //////////////////////////////////////////////////////////////////////
/*
The JsonCpp library's source code, including accompanying documentation,
tests and demonstration applications, are licensed under the following
conditions...
-The author (Baptiste Lepilleur) explicitly disclaims copyright in all
+Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
jurisdictions which recognize such a disclaimer. In such jurisdictions,
this software is released into the Public Domain.
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
-2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is
-released under the terms of the MIT License (see below).
+2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
+The JsonCpp Authors, and is released under the terms of the MIT License (see below).
In jurisdictions which recognize Public Domain property, the user of this
software may choose to accept it either as 1) Public Domain, 2) under the
conditions of the MIT License (see below), or 3) under the terms of dual
Public Domain/MIT License conditions described here, as they choose.
The MIT License is about as close to Public Domain as a license can get, and is
described in clear, concise terms at:
http://en.wikipedia.org/wiki/MIT_License
The full text of the MIT License follows:
========================================================================
-Copyright (c) 2007-2010 Baptiste Lepilleur
+Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
(END LICENSE TEXT)
The MIT license is compatible with both the GPL and commercial
software, affording one all of the rights of Public Domain with the
minor nuisance of being required to keep the above copyright notice
and license text in the source code. Note also that by accepting the
Public Domain "license" you can re-license your copy using whatever
license you like.
*/
// //////////////////////////////////////////////////////////////////////
// End of content of file: LICENSE
// //////////////////////////////////////////////////////////////////////
-#ifndef JSON_AMALGATED_H_INCLUDED
-# define JSON_AMALGATED_H_INCLUDED
-/// If defined, indicates that the source file is amalgated
+#ifndef JSON_AMALGAMATED_H_INCLUDED
+# define JSON_AMALGAMATED_H_INCLUDED
+/// If defined, indicates that the source file is amalgamated
/// to prevent private header inclusion.
-#define JSON_IS_AMALGATED
+#define JSON_IS_AMALGAMATION
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/version.h
+// //////////////////////////////////////////////////////////////////////
+
+#ifndef JSON_VERSION_H_INCLUDED
+#define JSON_VERSION_H_INCLUDED
+
+// Note: version must be updated in three places when doing a release. This
+// annoying process ensures that amalgamate, CMake, and meson all report the
+// correct version.
+// 1. /meson.build
+// 2. /include/json/version.h
+// 3. /CMakeLists.txt
+// IMPORTANT: also update the SOVERSION!!
+
+#define JSONCPP_VERSION_STRING "1.9.5"
+#define JSONCPP_VERSION_MAJOR 1
+#define JSONCPP_VERSION_MINOR 9
+#define JSONCPP_VERSION_PATCH 5
+#define JSONCPP_VERSION_QUALIFIER
+#define JSONCPP_VERSION_HEXA \
+ ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \
+ (JSONCPP_VERSION_PATCH << 8))
+
+#ifdef JSONCPP_USING_SECURE_MEMORY
+#undef JSONCPP_USING_SECURE_MEMORY
+#endif
+#define JSONCPP_USING_SECURE_MEMORY 0
+// If non-zero, the library zeroes any memory that it has allocated before
+// it frees its memory.
+
+#endif // JSON_VERSION_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/version.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/allocator.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_ALLOCATOR_H_INCLUDED
+#define JSON_ALLOCATOR_H_INCLUDED
+
+#include <cstring>
+#include <memory>
+
+#pragma pack(push, 8)
+
+namespace Json {
+template <typename T> class SecureAllocator {
+public:
+ // Type definitions
+ using value_type = T;
+ using pointer = T*;
+ using const_pointer = const T*;
+ using reference = T&;
+ using const_reference = const T&;
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+
+ /**
+ * Allocate memory for N items using the standard allocator.
+ */
+ pointer allocate(size_type n) {
+ // allocate using "global operator new"
+ return static_cast<pointer>(::operator new(n * sizeof(T)));
+ }
+
+ /**
+ * Release memory which was allocated for N items at pointer P.
+ *
+ * The memory block is filled with zeroes before being released.
+ */
+ void deallocate(pointer p, size_type n) {
+ // memset_s is used because memset may be optimized away by the compiler
+ memset_s(p, n * sizeof(T), 0, n * sizeof(T));
+ // free using "global operator delete"
+ ::operator delete(p);
+ }
+
+ /**
+ * Construct an item in-place at pointer P.
+ */
+ template <typename... Args> void construct(pointer p, Args&&... args) {
+ // construct using "placement new" and "perfect forwarding"
+ ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
+ }
+
+ size_type max_size() const { return size_t(-1) / sizeof(T); }
+
+ pointer address(reference x) const { return std::addressof(x); }
+
+ const_pointer address(const_reference x) const { return std::addressof(x); }
+
+ /**
+ * Destroy an item in-place at pointer P.
+ */
+ void destroy(pointer p) {
+ // destroy using "explicit destructor"
+ p->~T();
+ }
+
+ // Boilerplate
+ SecureAllocator() {}
+ template <typename U> SecureAllocator(const SecureAllocator<U>&) {}
+ template <typename U> struct rebind { using other = SecureAllocator<U>; };
+};
+
+template <typename T, typename U>
+bool operator==(const SecureAllocator<T>&, const SecureAllocator<U>&) {
+ return true;
+}
+
+template <typename T, typename U>
+bool operator!=(const SecureAllocator<T>&, const SecureAllocator<U>&) {
+ return false;
+}
+
+} // namespace Json
+
+#pragma pack(pop)
+
+#endif // JSON_ALLOCATOR_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/allocator.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: include/json/config.h
// //////////////////////////////////////////////////////////////////////
-// Copyright 2007-2010 Baptiste Lepilleur
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_CONFIG_H_INCLUDED
-# define JSON_CONFIG_H_INCLUDED
-
-/// If defined, indicates that json library is embedded in CppTL library.
-//# define JSON_IN_CPPTL 1
-
-/// If defined, indicates that json may leverage CppTL library
-//# define JSON_USE_CPPTL 1
-/// If defined, indicates that cpptl vector based map should be used instead of std::map
-/// as Value container.
-//# define JSON_USE_CPPTL_SMALLMAP 1
-/// If defined, indicates that Json specific container should be used
-/// (hash table & simple deque container with customizable allocator).
-/// THIS FEATURE IS STILL EXPERIMENTAL! There is know bugs: See #3177332
-//# define JSON_VALUE_USE_INTERNAL_MAP 1
-/// Force usage of standard new/malloc based allocator instead of memory pool based allocator.
-/// The memory pools allocator used optimization (initializing Value and ValueInternalLink
-/// as if it was a POD) that may cause some validation tool to report errors.
-/// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined.
-//# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1
-
-/// If defined, indicates that Json use exception to report invalid type manipulation
-/// instead of C assert macro.
-# define JSON_USE_EXCEPTION 1
-
-/// If defined, indicates that the source file is amalgated
+#define JSON_CONFIG_H_INCLUDED
+#include <cstddef>
+#include <cstdint>
+#include <istream>
+#include <memory>
+#include <ostream>
+#include <sstream>
+#include <string>
+#include <type_traits>
+
+// If non-zero, the library uses exceptions to report bad input instead of C
+// assertion macros. The default is to use exceptions.
+#ifndef JSON_USE_EXCEPTION
+#define JSON_USE_EXCEPTION 1
+#endif
+
+// Temporary, tracked for removal with issue #982.
+#ifndef JSON_USE_NULLREF
+#define JSON_USE_NULLREF 1
+#endif
+
+/// If defined, indicates that the source file is amalgamated
/// to prevent private header inclusion.
-/// Remarks: it is automatically defined in the generated amalgated header.
-#define JSON_IS_AMALGAMATION
+/// Remarks: it is automatically defined in the generated amalgamated header.
+// #define JSON_IS_AMALGAMATION
+
+// Export macros for DLL visibility
+#if defined(JSON_DLL_BUILD)
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#define JSON_API __declspec(dllexport)
+#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
+#elif defined(__GNUC__) || defined(__clang__)
+#define JSON_API __attribute__((visibility("default")))
+#endif // if defined(_MSC_VER)
+
+#elif defined(JSON_DLL)
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#define JSON_API __declspec(dllimport)
+#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
+#endif // if defined(_MSC_VER)
+#endif // ifdef JSON_DLL_BUILD
+
+#if !defined(JSON_API)
+#define JSON_API
+#endif
+#if defined(_MSC_VER) && _MSC_VER < 1800
+#error \
+ "ERROR: Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities"
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+// As recommended at
+// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
+extern JSON_API int msvc_pre1900_c99_snprintf(char* outBuf, size_t size,
+ const char* format, ...);
+#define jsoncpp_snprintf msvc_pre1900_c99_snprintf
+#else
+#define jsoncpp_snprintf std::snprintf
+#endif
-# ifdef JSON_IN_CPPTL
-# include <cpptl/config.h>
-# ifndef JSON_USE_CPPTL
-# define JSON_USE_CPPTL 1
-# endif
-# endif
-
-# ifdef JSON_IN_CPPTL
-# define JSON_API CPPTL_API
-# elif defined(JSON_DLL_BUILD)
-# define JSON_API __declspec(dllexport)
-# elif defined(JSON_DLL)
-# define JSON_API __declspec(dllimport)
-# else
-# define JSON_API
-# endif
-
-// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for integer
+// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
+// integer
// Storages, and 64 bits integer support is disabled.
// #define JSON_NO_INT64 1
-#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6
-// Microsoft Visual Studio 6 only support conversion from __int64 to double
-// (no conversion from unsigned __int64).
-#define JSON_USE_INT64_DOUBLE_CONVERSION 1
-#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6
+// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools.
+// C++11 should be used directly in JSONCPP.
+#define JSONCPP_OVERRIDE override
-#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008
-/// Indicates that the following function is deprecated.
-# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
+#ifdef __clang__
+#if __has_extension(attribute_deprecated_with_message)
+#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
#endif
+#elif defined(__GNUC__) // not clang (gcc comes later since clang emulates gcc)
+#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
+#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
+#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
+#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
+#endif // GNUC version
+#elif defined(_MSC_VER) // MSVC (after clang because clang on Windows emulates
+ // MSVC)
+#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
+#endif // __clang__ || __GNUC__ || _MSC_VER
#if !defined(JSONCPP_DEPRECATED)
-# define JSONCPP_DEPRECATED(message)
+#define JSONCPP_DEPRECATED(message)
#endif // if !defined(JSONCPP_DEPRECATED)
+#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6))
+#define JSON_USE_INT64_DOUBLE_CONVERSION 1
+#endif
+
+#if !defined(JSON_IS_AMALGAMATION)
+
+#include "allocator.h"
+#include "version.h"
+
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
namespace Json {
- typedef int Int;
- typedef unsigned int UInt;
-# if defined(JSON_NO_INT64)
- typedef int LargestInt;
- typedef unsigned int LargestUInt;
-# undef JSON_HAS_INT64
-# else // if defined(JSON_NO_INT64)
- // For Microsoft Visual use specific types as long long is not supported
-# if defined(_MSC_VER) // Microsoft Visual Studio
- typedef __int64 Int64;
- typedef unsigned __int64 UInt64;
-# else // if defined(_MSC_VER) // Other platforms, use long long
- typedef long long int Int64;
- typedef unsigned long long int UInt64;
-# endif // if defined(_MSC_VER)
- typedef Int64 LargestInt;
- typedef UInt64 LargestUInt;
-# define JSON_HAS_INT64
-# endif // if defined(JSON_NO_INT64)
-} // end namespace Json
+using Int = int;
+using UInt = unsigned int;
+#if defined(JSON_NO_INT64)
+using LargestInt = int;
+using LargestUInt = unsigned int;
+#undef JSON_HAS_INT64
+#else // if defined(JSON_NO_INT64)
+// For Microsoft Visual use specific types as long long is not supported
+#if defined(_MSC_VER) // Microsoft Visual Studio
+using Int64 = __int64;
+using UInt64 = unsigned __int64;
+#else // if defined(_MSC_VER) // Other platforms, use long long
+using Int64 = int64_t;
+using UInt64 = uint64_t;
+#endif // if defined(_MSC_VER)
+using LargestInt = Int64;
+using LargestUInt = UInt64;
+#define JSON_HAS_INT64
+#endif // if defined(JSON_NO_INT64)
+
+template <typename T>
+using Allocator =
+ typename std::conditional<JSONCPP_USING_SECURE_MEMORY, SecureAllocator<T>,
+ std::allocator<T>>::type;
+using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
+using IStringStream =
+ std::basic_istringstream<String::value_type, String::traits_type,
+ String::allocator_type>;
+using OStringStream =
+ std::basic_ostringstream<String::value_type, String::traits_type,
+ String::allocator_type>;
+using IStream = std::istream;
+using OStream = std::ostream;
+} // namespace Json
+// Legacy names (formerly macros).
+using JSONCPP_STRING = Json::String;
+using JSONCPP_ISTRINGSTREAM = Json::IStringStream;
+using JSONCPP_OSTRINGSTREAM = Json::OStringStream;
+using JSONCPP_ISTREAM = Json::IStream;
+using JSONCPP_OSTREAM = Json::OStream;
#endif // JSON_CONFIG_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
// End of content of file: include/json/config.h
// //////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: include/json/forwards.h
// //////////////////////////////////////////////////////////////////////
-// Copyright 2007-2010 Baptiste Lepilleur
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_FORWARDS_H_INCLUDED
-# define JSON_FORWARDS_H_INCLUDED
+#define JSON_FORWARDS_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
-# include "config.h"
+#include "config.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
- // writer.h
- class FastWriter;
- class StyledWriter;
-
- // reader.h
- class Reader;
-
- // features.h
- class Features;
-
- // value.h
- typedef unsigned int ArrayIndex;
- class StaticString;
- class Path;
- class PathArgument;
- class Value;
- class ValueIteratorBase;
- class ValueIterator;
- class ValueConstIterator;
-#ifdef JSON_VALUE_USE_INTERNAL_MAP
- class ValueMapAllocator;
- class ValueInternalLink;
- class ValueInternalArray;
- class ValueInternalMap;
-#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP
+// writer.h
+class StreamWriter;
+class StreamWriterBuilder;
+class Writer;
+class FastWriter;
+class StyledWriter;
+class StyledStreamWriter;
+
+// reader.h
+class Reader;
+class CharReader;
+class CharReaderBuilder;
+
+// json_features.h
+class Features;
+
+// value.h
+using ArrayIndex = unsigned int;
+class StaticString;
+class Path;
+class PathArgument;
+class Value;
+class ValueIteratorBase;
+class ValueIterator;
+class ValueConstIterator;
} // namespace Json
-
#endif // JSON_FORWARDS_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
// End of content of file: include/json/forwards.h
// //////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: include/json/features.h
+// Beginning of content of file: include/json/json_features.h
// //////////////////////////////////////////////////////////////////////
-// Copyright 2007-2010 Baptiste Lepilleur
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-#ifndef CPPTL_JSON_FEATURES_H_INCLUDED
-# define CPPTL_JSON_FEATURES_H_INCLUDED
+#ifndef JSON_FEATURES_H_INCLUDED
+#define JSON_FEATURES_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
-# include "forwards.h"
+#include "forwards.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
+#pragma pack(push, 8)
+
namespace Json {
- /** \brief Configuration passed to reader and writer.
- * This configuration object can be used to force the Reader or Writer
- * to behave in a standard conforming way.
- */
- class JSON_API Features
- {
- public:
- /** \brief A configuration that allows all features and assumes all strings are UTF-8.
- * - C & C++ comments are allowed
- * - Root object can be any JSON value
- * - Assumes Value strings are encoded in UTF-8
- */
- static Features all();
-
- /** \brief A configuration that is strictly compatible with the JSON specification.
- * - Comments are forbidden.
- * - Root object must be either an array or an object value.
- * - Assumes Value strings are encoded in UTF-8
- */
- static Features strictMode();
-
- /** \brief Initialize the configuration like JsonConfig::allFeatures;
- */
- Features();
-
- /// \c true if comments are allowed. Default: \c true.
- bool allowComments_;
-
- /// \c true if root must be either an array or an object value. Default: \c false.
- bool strictRoot_;
- };
+/** \brief Configuration passed to reader and writer.
+ * This configuration object can be used to force the Reader or Writer
+ * to behave in a standard conforming way.
+ */
+class JSON_API Features {
+public:
+ /** \brief A configuration that allows all features and assumes all strings
+ * are UTF-8.
+ * - C & C++ comments are allowed
+ * - Root object can be any JSON value
+ * - Assumes Value strings are encoded in UTF-8
+ */
+ static Features all();
+
+ /** \brief A configuration that is strictly compatible with the JSON
+ * specification.
+ * - Comments are forbidden.
+ * - Root object must be either an array or an object value.
+ * - Assumes Value strings are encoded in UTF-8
+ */
+ static Features strictMode();
+
+ /** \brief Initialize the configuration like JsonConfig::allFeatures;
+ */
+ Features();
+
+ /// \c true if comments are allowed. Default: \c true.
+ bool allowComments_{true};
+
+ /// \c true if root must be either an array or an object value. Default: \c
+ /// false.
+ bool strictRoot_{false};
+
+ /// \c true if dropped null placeholders are allowed. Default: \c false.
+ bool allowDroppedNullPlaceholders_{false};
+
+ /// \c true if numeric object key are allowed. Default: \c false.
+ bool allowNumericKeys_{false};
+};
} // namespace Json
-#endif // CPPTL_JSON_FEATURES_H_INCLUDED
+#pragma pack(pop)
+
+#endif // JSON_FEATURES_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
-// End of content of file: include/json/features.h
+// End of content of file: include/json/json_features.h
// //////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: include/json/value.h
// //////////////////////////////////////////////////////////////////////
-// Copyright 2007-2010 Baptiste Lepilleur
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-#ifndef CPPTL_JSON_H_INCLUDED
-# define CPPTL_JSON_H_INCLUDED
+#ifndef JSON_H_INCLUDED
+#define JSON_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
-# include "forwards.h"
+#include "forwards.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
-# include <string>
-# include <vector>
-
-# ifndef JSON_USE_CPPTL_SMALLMAP
-# include <map>
-# else
-# include <cpptl/smallmap.h>
-# endif
-# ifdef JSON_USE_CPPTL
-# include <cpptl/forwards.h>
-# endif
+
+// Conditional NORETURN attribute on the throw functions would:
+// a) suppress false positives from static code analysis
+// b) possibly improve optimization opportunities.
+#if !defined(JSONCPP_NORETURN)
+#if defined(_MSC_VER) && _MSC_VER == 1800
+#define JSONCPP_NORETURN __declspec(noreturn)
+#else
+#define JSONCPP_NORETURN [[noreturn]]
+#endif
+#endif
+
+// Support for '= delete' with template declarations was a late addition
+// to the c++11 standard and is rejected by clang 3.8 and Apple clang 8.2
+// even though these declare themselves to be c++11 compilers.
+#if !defined(JSONCPP_TEMPLATE_DELETE)
+#if defined(__clang__) && defined(__apple_build_version__)
+#if __apple_build_version__ <= 8000042
+#define JSONCPP_TEMPLATE_DELETE
+#endif
+#elif defined(__clang__)
+#if __clang_major__ == 3 && __clang_minor__ <= 8
+#define JSONCPP_TEMPLATE_DELETE
+#endif
+#endif
+#if !defined(JSONCPP_TEMPLATE_DELETE)
+#define JSONCPP_TEMPLATE_DELETE = delete
+#endif
+#endif
+
+#include <array>
+#include <exception>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+// Disable warning C4251: <data member>: <type> needs to have dll-interface to
+// be used by...
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(push)
+#pragma warning(disable : 4251 4275)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#pragma pack(push, 8)
/** \brief JSON (JavaScript Object Notation).
*/
namespace Json {
- /** \brief Type of the value held by a Value object.
- */
- enum ValueType
- {
- nullValue = 0, ///< 'null' value
- intValue, ///< signed integer value
- uintValue, ///< unsigned integer value
- realValue, ///< double value
- stringValue, ///< UTF-8 string value
- booleanValue, ///< bool value
- arrayValue, ///< array value (ordered list)
- objectValue ///< object value (collection of name/value pairs).
- };
-
- enum CommentPlacement
- {
- commentBefore = 0, ///< a comment placed on the line before a value
- commentAfterOnSameLine, ///< a comment just after a value on the same line
- commentAfter, ///< a comment on the line after a value (only make sense for root value)
- numberOfCommentPlacement
- };
-
-//# ifdef JSON_USE_CPPTL
-// typedef CppTL::AnyEnumerator<const char *> EnumMemberNames;
-// typedef CppTL::AnyEnumerator<const Value &> EnumValues;
-//# endif
-
- /** \brief Lightweight wrapper to tag static string.
- *
- * Value constructor and objectValue member assignement takes advantage of the
- * StaticString and avoid the cost of string duplication when storing the
- * string or the member name.
- *
- * Example of usage:
- * \code
- * Json::Value aValue( StaticString("some text") );
- * Json::Value object;
- * static const StaticString code("code");
- * object[code] = 1234;
- * \endcode
- */
- class JSON_API StaticString
- {
- public:
- explicit StaticString( const char *czstring )
- : str_( czstring )
- {
- }
-
- operator const char *() const
- {
- return str_;
- }
-
- const char *c_str() const
- {
- return str_;
- }
-
- private:
- const char *str_;
- };
-
- /** \brief Represents a <a HREF="http://www.json.org">JSON</a> value.
- *
- * This class is a discriminated union wrapper that can represents a:
- * - signed integer [range: Value::minInt - Value::maxInt]
- * - unsigned integer (range: 0 - Value::maxUInt)
- * - double
- * - UTF-8 string
- * - boolean
- * - 'null'
- * - an ordered list of Value
- * - collection of name/value pairs (javascript object)
- *
- * The type of the held value is represented by a #ValueType and
- * can be obtained using type().
- *
- * values of an #objectValue or #arrayValue can be accessed using operator[]() methods.
- * Non const methods will automatically create the a #nullValue element
- * if it does not exist.
- * The sequence of an #arrayValue will be automatically resize and initialized
- * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue.
- *
- * The get() methods can be used to obtanis default value in the case the required element
- * does not exist.
- *
- * It is possible to iterate over the list of a #objectValue values using
- * the getMemberNames() method.
- */
- class JSON_API Value
- {
- friend class ValueIteratorBase;
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
- friend class ValueInternalLink;
- friend class ValueInternalMap;
-# endif
- public:
- typedef std::vector<std::string> Members;
- typedef ValueIterator iterator;
- typedef ValueConstIterator const_iterator;
- typedef Json::UInt UInt;
- typedef Json::Int Int;
-# if defined(JSON_HAS_INT64)
- typedef Json::UInt64 UInt64;
- typedef Json::Int64 Int64;
+#if JSON_USE_EXCEPTION
+/** Base class for all exceptions we throw.
+ *
+ * We use nothing but these internally. Of course, STL can throw others.
+ */
+class JSON_API Exception : public std::exception {
+public:
+ Exception(String msg);
+ ~Exception() noexcept override;
+ char const* what() const noexcept override;
+
+protected:
+ String msg_;
+};
+
+/** Exceptions which the user cannot easily avoid.
+ *
+ * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input
+ *
+ * \remark derived from Json::Exception
+ */
+class JSON_API RuntimeError : public Exception {
+public:
+ RuntimeError(String const& msg);
+};
+
+/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros.
+ *
+ * These are precondition-violations (user bugs) and internal errors (our bugs).
+ *
+ * \remark derived from Json::Exception
+ */
+class JSON_API LogicError : public Exception {
+public:
+ LogicError(String const& msg);
+};
+#endif
+
+/// used internally
+JSONCPP_NORETURN void throwRuntimeError(String const& msg);
+/// used internally
+JSONCPP_NORETURN void throwLogicError(String const& msg);
+
+/** \brief Type of the value held by a Value object.
+ */
+enum ValueType {
+ nullValue = 0, ///< 'null' value
+ intValue, ///< signed integer value
+ uintValue, ///< unsigned integer value
+ realValue, ///< double value
+ stringValue, ///< UTF-8 string value
+ booleanValue, ///< bool value
+ arrayValue, ///< array value (ordered list)
+ objectValue ///< object value (collection of name/value pairs).
+};
+
+enum CommentPlacement {
+ commentBefore = 0, ///< a comment placed on the line before a value
+ commentAfterOnSameLine, ///< a comment just after a value on the same line
+ commentAfter, ///< a comment on the line after a value (only make sense for
+ /// root value)
+ numberOfCommentPlacement
+};
+
+/** \brief Type of precision for formatting of real values.
+ */
+enum PrecisionType {
+ significantDigits = 0, ///< we set max number of significant digits in string
+ decimalPlaces ///< we set max number of digits after "." in string
+};
+
+/** \brief Lightweight wrapper to tag static string.
+ *
+ * Value constructor and objectValue member assignment takes advantage of the
+ * StaticString and avoid the cost of string duplication when storing the
+ * string or the member name.
+ *
+ * Example of usage:
+ * \code
+ * Json::Value aValue( StaticString("some text") );
+ * Json::Value object;
+ * static const StaticString code("code");
+ * object[code] = 1234;
+ * \endcode
+ */
+class JSON_API StaticString {
+public:
+ explicit StaticString(const char* czstring) : c_str_(czstring) {}
+
+ operator const char*() const { return c_str_; }
+
+ const char* c_str() const { return c_str_; }
+
+private:
+ const char* c_str_;
+};
+
+/** \brief Represents a <a HREF="http://www.json.org">JSON</a> value.
+ *
+ * This class is a discriminated union wrapper that can represents a:
+ * - signed integer [range: Value::minInt - Value::maxInt]
+ * - unsigned integer (range: 0 - Value::maxUInt)
+ * - double
+ * - UTF-8 string
+ * - boolean
+ * - 'null'
+ * - an ordered list of Value
+ * - collection of name/value pairs (javascript object)
+ *
+ * The type of the held value is represented by a #ValueType and
+ * can be obtained using type().
+ *
+ * Values of an #objectValue or #arrayValue can be accessed using operator[]()
+ * methods.
+ * Non-const methods will automatically create the a #nullValue element
+ * if it does not exist.
+ * The sequence of an #arrayValue will be automatically resized and initialized
+ * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue.
+ *
+ * The get() methods can be used to obtain default value in the case the
+ * required element does not exist.
+ *
+ * It is possible to iterate over the list of member keys of an object using
+ * the getMemberNames() method.
+ *
+ * \note #Value string-length fit in size_t, but keys must be < 2^30.
+ * (The reason is an implementation detail.) A #CharReader will raise an
+ * exception if a bound is exceeded to avoid security holes in your app,
+ * but the Value API does *not* check bounds. That is the responsibility
+ * of the caller.
+ */
+class JSON_API Value {
+ friend class ValueIteratorBase;
+
+public:
+ using Members = std::vector<String>;
+ using iterator = ValueIterator;
+ using const_iterator = ValueConstIterator;
+ using UInt = Json::UInt;
+ using Int = Json::Int;
+#if defined(JSON_HAS_INT64)
+ using UInt64 = Json::UInt64;
+ using Int64 = Json::Int64;
+#endif // defined(JSON_HAS_INT64)
+ using LargestInt = Json::LargestInt;
+ using LargestUInt = Json::LargestUInt;
+ using ArrayIndex = Json::ArrayIndex;
+
+ // Required for boost integration, e. g. BOOST_TEST
+ using value_type = std::string;
+
+#if JSON_USE_NULLREF
+ // Binary compatibility kludges, do not use.
+ static const Value& null;
+ static const Value& nullRef;
+#endif
+
+ // null and nullRef are deprecated, use this instead.
+ static Value const& nullSingleton();
+
+ /// Minimum signed integer value that can be stored in a Json::Value.
+ static constexpr LargestInt minLargestInt =
+ LargestInt(~(LargestUInt(-1) / 2));
+ /// Maximum signed integer value that can be stored in a Json::Value.
+ static constexpr LargestInt maxLargestInt = LargestInt(LargestUInt(-1) / 2);
+ /// Maximum unsigned integer value that can be stored in a Json::Value.
+ static constexpr LargestUInt maxLargestUInt = LargestUInt(-1);
+
+ /// Minimum signed int value that can be stored in a Json::Value.
+ static constexpr Int minInt = Int(~(UInt(-1) / 2));
+ /// Maximum signed int value that can be stored in a Json::Value.
+ static constexpr Int maxInt = Int(UInt(-1) / 2);
+ /// Maximum unsigned int value that can be stored in a Json::Value.
+ static constexpr UInt maxUInt = UInt(-1);
+
+#if defined(JSON_HAS_INT64)
+ /// Minimum signed 64 bits int value that can be stored in a Json::Value.
+ static constexpr Int64 minInt64 = Int64(~(UInt64(-1) / 2));
+ /// Maximum signed 64 bits int value that can be stored in a Json::Value.
+ static constexpr Int64 maxInt64 = Int64(UInt64(-1) / 2);
+ /// Maximum unsigned 64 bits int value that can be stored in a Json::Value.
+ static constexpr UInt64 maxUInt64 = UInt64(-1);
#endif // defined(JSON_HAS_INT64)
- typedef Json::LargestInt LargestInt;
- typedef Json::LargestUInt LargestUInt;
- typedef Json::ArrayIndex ArrayIndex;
-
- static const Value null;
- /// Minimum signed integer value that can be stored in a Json::Value.
- static const LargestInt minLargestInt;
- /// Maximum signed integer value that can be stored in a Json::Value.
- static const LargestInt maxLargestInt;
- /// Maximum unsigned integer value that can be stored in a Json::Value.
- static const LargestUInt maxLargestUInt;
-
- /// Minimum signed int value that can be stored in a Json::Value.
- static const Int minInt;
- /// Maximum signed int value that can be stored in a Json::Value.
- static const Int maxInt;
- /// Maximum unsigned int value that can be stored in a Json::Value.
- static const UInt maxUInt;
-
- /// Minimum signed 64 bits int value that can be stored in a Json::Value.
- static const Int64 minInt64;
- /// Maximum signed 64 bits int value that can be stored in a Json::Value.
- static const Int64 maxInt64;
- /// Maximum unsigned 64 bits int value that can be stored in a Json::Value.
- static const UInt64 maxUInt64;
-
- private:
+ /// Default precision for real value for string representation.
+ static constexpr UInt defaultRealPrecision = 17;
+ // The constant is hard-coded because some compiler have trouble
+ // converting Value::maxUInt64 to a double correctly (AIX/xlC).
+ // Assumes that UInt64 is a 64 bits integer.
+ static constexpr double maxUInt64AsDouble = 18446744073709551615.0;
+// Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler
+// when using gcc and clang backend compilers. CZString
+// cannot be defined as private. See issue #486
+#ifdef __NVCC__
+public:
+#else
+private:
+#endif
#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
-# ifndef JSON_VALUE_USE_INTERNAL_MAP
- class CZString
- {
- public:
- enum DuplicationPolicy
- {
- noDuplication = 0,
- duplicate,
- duplicateOnCopy
- };
- CZString( ArrayIndex index );
- CZString( const char *cstr, DuplicationPolicy allocate );
- CZString( const CZString &other );
- ~CZString();
- CZString &operator =( const CZString &other );
- bool operator<( const CZString &other ) const;
- bool operator==( const CZString &other ) const;
- ArrayIndex index() const;
- const char *c_str() const;
- bool isStaticString() const;
- private:
- void swap( CZString &other );
- const char *cstr_;
- ArrayIndex index_;
- };
-
- public:
-# ifndef JSON_USE_CPPTL_SMALLMAP
- typedef std::map<CZString, Value> ObjectValues;
-# else
- typedef CppTL::SmallMap<CZString, Value> ObjectValues;
-# endif // ifndef JSON_USE_CPPTL_SMALLMAP
-# endif // ifndef JSON_VALUE_USE_INTERNAL_MAP
+ class CZString {
+ public:
+ enum DuplicationPolicy { noDuplication = 0, duplicate, duplicateOnCopy };
+ CZString(ArrayIndex index);
+ CZString(char const* str, unsigned length, DuplicationPolicy allocate);
+ CZString(CZString const& other);
+ CZString(CZString&& other) noexcept;
+ ~CZString();
+ CZString& operator=(const CZString& other);
+ CZString& operator=(CZString&& other) noexcept;
+
+ bool operator<(CZString const& other) const;
+ bool operator==(CZString const& other) const;
+ ArrayIndex index() const;
+ // const char* c_str() const; ///< \deprecated
+ char const* data() const;
+ unsigned length() const;
+ bool isStaticString() const;
+
+ private:
+ void swap(CZString& other);
+
+ struct StringStorage {
+ unsigned policy_ : 2;
+ unsigned length_ : 30; // 1GB max
+ };
+
+ char const* cstr_; // actually, a prefixed string, unless policy is noDup
+ union {
+ ArrayIndex index_;
+ StringStorage storage_;
+ };
+ };
+
+public:
+ typedef std::map<CZString, Value> ObjectValues;
#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
- public:
- /** \brief Create a default Value of the given type.
-
- This is a very useful constructor.
- To create an empty array, pass arrayValue.
- To create an empty object, pass objectValue.
- Another Value can then be set to this one by assignment.
- This is useful since clear() and resize() will not alter types.
-
- Examples:
- \code
- Json::Value null_value; // null
- Json::Value arr_value(Json::arrayValue); // []
- Json::Value obj_value(Json::objectValue); // {}
- \endcode
- */
- Value( ValueType type = nullValue );
- Value( Int value );
- Value( UInt value );
+public:
+ /**
+ * \brief Create a default Value of the given type.
+ *
+ * This is a very useful constructor.
+ * To create an empty array, pass arrayValue.
+ * To create an empty object, pass objectValue.
+ * Another Value can then be set to this one by assignment.
+ * This is useful since clear() and resize() will not alter types.
+ *
+ * Examples:
+ * \code
+ * Json::Value null_value; // null
+ * Json::Value arr_value(Json::arrayValue); // []
+ * Json::Value obj_value(Json::objectValue); // {}
+ * \endcode
+ */
+ Value(ValueType type = nullValue);
+ Value(Int value);
+ Value(UInt value);
#if defined(JSON_HAS_INT64)
- Value( Int64 value );
- Value( UInt64 value );
+ Value(Int64 value);
+ Value(UInt64 value);
#endif // if defined(JSON_HAS_INT64)
- Value( double value );
- Value( const char *value );
- Value( const char *beginValue, const char *endValue );
- /** \brief Constructs a value from a static string.
-
- * Like other value string constructor but do not duplicate the string for
- * internal storage. The given string must remain alive after the call to this
- * constructor.
- * Example of usage:
- * \code
- * Json::Value aValue( StaticString("some text") );
- * \endcode
- */
- Value( const StaticString &value );
- Value( const std::string &value );
-# ifdef JSON_USE_CPPTL
- Value( const CppTL::ConstString &value );
-# endif
- Value( bool value );
- Value( const Value &other );
- ~Value();
-
- Value &operator=( const Value &other );
- /// Swap values.
- /// \note Currently, comments are intentionally not swapped, for
- /// both logic and efficiency.
- void swap( Value &other );
-
- ValueType type() const;
-
- bool operator <( const Value &other ) const;
- bool operator <=( const Value &other ) const;
- bool operator >=( const Value &other ) const;
- bool operator >( const Value &other ) const;
-
- bool operator ==( const Value &other ) const;
- bool operator !=( const Value &other ) const;
-
- int compare( const Value &other ) const;
-
- const char *asCString() const;
- std::string asString() const;
-# ifdef JSON_USE_CPPTL
- CppTL::ConstString asConstString() const;
-# endif
- Int asInt() const;
- UInt asUInt() const;
- Int64 asInt64() const;
- UInt64 asUInt64() const;
- LargestInt asLargestInt() const;
- LargestUInt asLargestUInt() const;
- float asFloat() const;
- double asDouble() const;
- bool asBool() const;
-
- bool isNull() const;
- bool isBool() const;
- bool isInt() const;
- bool isUInt() const;
- bool isIntegral() const;
- bool isDouble() const;
- bool isNumeric() const;
- bool isString() const;
- bool isArray() const;
- bool isObject() const;
-
- bool isConvertibleTo( ValueType other ) const;
-
- /// Number of values in array or object
- ArrayIndex size() const;
-
- /// \brief Return true if empty array, empty object, or null;
- /// otherwise, false.
- bool empty() const;
-
- /// Return isNull()
- bool operator!() const;
-
- /// Remove all object members and array elements.
- /// \pre type() is arrayValue, objectValue, or nullValue
- /// \post type() is unchanged
- void clear();
-
- /// Resize the array to size elements.
- /// New elements are initialized to null.
- /// May only be called on nullValue or arrayValue.
- /// \pre type() is arrayValue or nullValue
- /// \post type() is arrayValue
- void resize( ArrayIndex size );
-
- /// Access an array element (zero based index ).
- /// If the array contains less than index element, then null value are inserted
- /// in the array so that its size is index+1.
- /// (You may need to say 'value[0u]' to get your compiler to distinguish
- /// this from the operator[] which takes a string.)
- Value &operator[]( ArrayIndex index );
-
- /// Access an array element (zero based index ).
- /// If the array contains less than index element, then null value are inserted
- /// in the array so that its size is index+1.
- /// (You may need to say 'value[0u]' to get your compiler to distinguish
- /// this from the operator[] which takes a string.)
- Value &operator[]( int index );
-
- /// Access an array element (zero based index )
- /// (You may need to say 'value[0u]' to get your compiler to distinguish
- /// this from the operator[] which takes a string.)
- const Value &operator[]( ArrayIndex index ) const;
-
- /// Access an array element (zero based index )
- /// (You may need to say 'value[0u]' to get your compiler to distinguish
- /// this from the operator[] which takes a string.)
- const Value &operator[]( int index ) const;
-
- /// If the array contains at least index+1 elements, returns the element value,
- /// otherwise returns defaultValue.
- Value get( ArrayIndex index,
- const Value &defaultValue ) const;
- /// Return true if index < size().
- bool isValidIndex( ArrayIndex index ) const;
- /// \brief Append value to array at the end.
- ///
- /// Equivalent to jsonvalue[jsonvalue.size()] = value;
- Value &append( const Value &value );
-
- /// Access an object value by name, create a null member if it does not exist.
- Value &operator[]( const char *key );
- /// Access an object value by name, returns null if there is no member with that name.
- const Value &operator[]( const char *key ) const;
- /// Access an object value by name, create a null member if it does not exist.
- Value &operator[]( const std::string &key );
- /// Access an object value by name, returns null if there is no member with that name.
- const Value &operator[]( const std::string &key ) const;
- /** \brief Access an object value by name, create a null member if it does not exist.
-
- * If the object as no entry for that name, then the member name used to store
- * the new entry is not duplicated.
- * Example of use:
- * \code
- * Json::Value object;
- * static const StaticString code("code");
- * object[code] = 1234;
- * \endcode
- */
- Value &operator[]( const StaticString &key );
-# ifdef JSON_USE_CPPTL
- /// Access an object value by name, create a null member if it does not exist.
- Value &operator[]( const CppTL::ConstString &key );
- /// Access an object value by name, returns null if there is no member with that name.
- const Value &operator[]( const CppTL::ConstString &key ) const;
-# endif
- /// Return the member named key if it exist, defaultValue otherwise.
- Value get( const char *key,
- const Value &defaultValue ) const;
- /// Return the member named key if it exist, defaultValue otherwise.
- Value get( const std::string &key,
- const Value &defaultValue ) const;
-# ifdef JSON_USE_CPPTL
- /// Return the member named key if it exist, defaultValue otherwise.
- Value get( const CppTL::ConstString &key,
- const Value &defaultValue ) const;
-# endif
- /// \brief Remove and return the named member.
- ///
- /// Do nothing if it did not exist.
- /// \return the removed Value, or null.
- /// \pre type() is objectValue or nullValue
- /// \post type() is unchanged
- Value removeMember( const char* key );
- /// Same as removeMember(const char*)
- Value removeMember( const std::string &key );
-
- /// Return true if the object has a member named key.
- bool isMember( const char *key ) const;
- /// Return true if the object has a member named key.
- bool isMember( const std::string &key ) const;
-# ifdef JSON_USE_CPPTL
- /// Return true if the object has a member named key.
- bool isMember( const CppTL::ConstString &key ) const;
-# endif
-
- /// \brief Return a list of the member names.
- ///
- /// If null, return an empty list.
- /// \pre type() is objectValue or nullValue
- /// \post if type() was nullValue, it remains nullValue
- Members getMemberNames() const;
-
-//# ifdef JSON_USE_CPPTL
-// EnumMemberNames enumMemberNames() const;
-// EnumValues enumValues() const;
-//# endif
-
- /// Comments must be //... or /* ... */
- void setComment( const char *comment,
- CommentPlacement placement );
- /// Comments must be //... or /* ... */
- void setComment( const std::string &comment,
- CommentPlacement placement );
- bool hasComment( CommentPlacement placement ) const;
- /// Include delimiters and embedded newlines.
- std::string getComment( CommentPlacement placement ) const;
-
- std::string toStyledString() const;
-
- const_iterator begin() const;
- const_iterator end() const;
-
- iterator begin();
- iterator end();
-
- private:
- Value &resolveReference( const char *key,
- bool isStatic );
-
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
- inline bool isItemAvailable() const
- {
- return itemIsUsed_ == 0;
- }
-
- inline void setItemUsed( bool isUsed = true )
- {
- itemIsUsed_ = isUsed ? 1 : 0;
- }
-
- inline bool isMemberNameStatic() const
- {
- return memberNameIsStatic_ == 0;
- }
-
- inline void setMemberNameIsStatic( bool isStatic )
- {
- memberNameIsStatic_ = isStatic ? 1 : 0;
- }
-# endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP
-
- private:
- struct CommentInfo
- {
- CommentInfo();
- ~CommentInfo();
-
- void setComment( const char *text );
-
- char *comment_;
- };
-
- //struct MemberNamesTransform
- //{
- // typedef const char *result_type;
- // const char *operator()( const CZString &name ) const
- // {
- // return name.c_str();
- // }
- //};
-
- union ValueHolder
- {
- LargestInt int_;
- LargestUInt uint_;
- double real_;
- bool bool_;
- char *string_;
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
- ValueInternalArray *array_;
- ValueInternalMap *map_;
-#else
- ObjectValues *map_;
-# endif
- } value_;
- ValueType type_ : 8;
- int allocated_ : 1; // Notes: if declared as bool, bitfield is useless.
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
- unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container.
- int memberNameIsStatic_ : 1; // used by the ValueInternalMap container.
-# endif
- CommentInfo *comments_;
- };
-
-
- /** \brief Experimental and untested: represents an element of the "path" to access a node.
- */
- class PathArgument
- {
- public:
- friend class Path;
-
- PathArgument();
- PathArgument( ArrayIndex index );
- PathArgument( const char *key );
- PathArgument( const std::string &key );
-
- private:
- enum Kind
- {
- kindNone = 0,
- kindIndex,
- kindKey
- };
- std::string key_;
- ArrayIndex index_;
- Kind kind_;
- };
-
- /** \brief Experimental and untested: represents a "path" to access a node.
- *
- * Syntax:
- * - "." => root node
- * - ".[n]" => elements at index 'n' of root node (an array value)
- * - ".name" => member named 'name' of root node (an object value)
- * - ".name1.name2.name3"
- * - ".[0][1][2].name1[3]"
- * - ".%" => member name is provided as parameter
- * - ".[%]" => index is provied as parameter
- */
- class Path
- {
- public:
- Path( const std::string &path,
- const PathArgument &a1 = PathArgument(),
- const PathArgument &a2 = PathArgument(),
- const PathArgument &a3 = PathArgument(),
- const PathArgument &a4 = PathArgument(),
- const PathArgument &a5 = PathArgument() );
-
- const Value &resolve( const Value &root ) const;
- Value resolve( const Value &root,
- const Value &defaultValue ) const;
- /// Creates the "path" to access the specified node and returns a reference on the node.
- Value &make( Value &root ) const;
-
- private:
- typedef std::vector<const PathArgument *> InArgs;
- typedef std::vector<PathArgument> Args;
-
- void makePath( const std::string &path,
- const InArgs &in );
- void addPathInArg( const std::string &path,
- const InArgs &in,
- InArgs::const_iterator &itInArg,
- PathArgument::Kind kind );
- void invalidPath( const std::string &path,
- int location );
-
- Args args_;
- };
-
-
-
-#ifdef JSON_VALUE_USE_INTERNAL_MAP
- /** \brief Allocator to customize Value internal map.
- * Below is an example of a simple implementation (default implementation actually
- * use memory pool for speed).
- * \code
- class DefaultValueMapAllocator : public ValueMapAllocator
- {
- public: // overridden from ValueMapAllocator
- virtual ValueInternalMap *newMap()
- {
- return new ValueInternalMap();
- }
-
- virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other )
- {
- return new ValueInternalMap( other );
- }
-
- virtual void destructMap( ValueInternalMap *map )
- {
- delete map;
- }
-
- virtual ValueInternalLink *allocateMapBuckets( unsigned int size )
- {
- return new ValueInternalLink[size];
- }
-
- virtual void releaseMapBuckets( ValueInternalLink *links )
- {
- delete [] links;
- }
-
- virtual ValueInternalLink *allocateMapLink()
- {
- return new ValueInternalLink();
- }
-
- virtual void releaseMapLink( ValueInternalLink *link )
- {
- delete link;
- }
- };
- * \endcode
- */
- class JSON_API ValueMapAllocator
- {
- public:
- virtual ~ValueMapAllocator();
- virtual ValueInternalMap *newMap() = 0;
- virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) = 0;
- virtual void destructMap( ValueInternalMap *map ) = 0;
- virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) = 0;
- virtual void releaseMapBuckets( ValueInternalLink *links ) = 0;
- virtual ValueInternalLink *allocateMapLink() = 0;
- virtual void releaseMapLink( ValueInternalLink *link ) = 0;
- };
-
- /** \brief ValueInternalMap hash-map bucket chain link (for internal use only).
- * \internal previous_ & next_ allows for bidirectional traversal.
- */
- class JSON_API ValueInternalLink
- {
- public:
- enum { itemPerLink = 6 }; // sizeof(ValueInternalLink) = 128 on 32 bits architecture.
- enum InternalFlags {
- flagAvailable = 0,
- flagUsed = 1
- };
-
- ValueInternalLink();
-
- ~ValueInternalLink();
-
- Value items_[itemPerLink];
- char *keys_[itemPerLink];
- ValueInternalLink *previous_;
- ValueInternalLink *next_;
- };
-
-
- /** \brief A linked page based hash-table implementation used internally by Value.
- * \internal ValueInternalMap is a tradional bucket based hash-table, with a linked
- * list in each bucket to handle collision. There is an addional twist in that
- * each node of the collision linked list is a page containing a fixed amount of
- * value. This provides a better compromise between memory usage and speed.
- *
- * Each bucket is made up of a chained list of ValueInternalLink. The last
- * link of a given bucket can be found in the 'previous_' field of the following bucket.
- * The last link of the last bucket is stored in tailLink_ as it has no following bucket.
- * Only the last link of a bucket may contains 'available' item. The last link always
- * contains at least one element unless is it the bucket one very first link.
- */
- class JSON_API ValueInternalMap
- {
- friend class ValueIteratorBase;
- friend class Value;
- public:
- typedef unsigned int HashKey;
- typedef unsigned int BucketIndex;
-
-# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
- struct IteratorState
- {
- IteratorState()
- : map_(0)
- , link_(0)
- , itemIndex_(0)
- , bucketIndex_(0)
- {
- }
- ValueInternalMap *map_;
- ValueInternalLink *link_;
- BucketIndex itemIndex_;
- BucketIndex bucketIndex_;
- };
-# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
-
- ValueInternalMap();
- ValueInternalMap( const ValueInternalMap &other );
- ValueInternalMap &operator =( const ValueInternalMap &other );
- ~ValueInternalMap();
-
- void swap( ValueInternalMap &other );
-
- BucketIndex size() const;
-
- void clear();
-
- bool reserveDelta( BucketIndex growth );
-
- bool reserve( BucketIndex newItemCount );
-
- const Value *find( const char *key ) const;
-
- Value *find( const char *key );
-
- Value &resolveReference( const char *key,
- bool isStatic );
-
- void remove( const char *key );
-
- void doActualRemove( ValueInternalLink *link,
- BucketIndex index,
- BucketIndex bucketIndex );
-
- ValueInternalLink *&getLastLinkInBucket( BucketIndex bucketIndex );
-
- Value &setNewItem( const char *key,
- bool isStatic,
- ValueInternalLink *link,
- BucketIndex index );
-
- Value &unsafeAdd( const char *key,
- bool isStatic,
- HashKey hashedKey );
-
- HashKey hash( const char *key ) const;
-
- int compare( const ValueInternalMap &other ) const;
-
- private:
- void makeBeginIterator( IteratorState &it ) const;
- void makeEndIterator( IteratorState &it ) const;
- static bool equals( const IteratorState &x, const IteratorState &other );
- static void increment( IteratorState &iterator );
- static void incrementBucket( IteratorState &iterator );
- static void decrement( IteratorState &iterator );
- static const char *key( const IteratorState &iterator );
- static const char *key( const IteratorState &iterator, bool &isStatic );
- static Value &value( const IteratorState &iterator );
- static int distance( const IteratorState &x, const IteratorState &y );
-
- private:
- ValueInternalLink *buckets_;
- ValueInternalLink *tailLink_;
- BucketIndex bucketsSize_;
- BucketIndex itemCount_;
- };
-
- /** \brief A simplified deque implementation used internally by Value.
- * \internal
- * It is based on a list of fixed "page", each page contains a fixed number of items.
- * Instead of using a linked-list, a array of pointer is used for fast item look-up.
- * Look-up for an element is as follow:
- * - compute page index: pageIndex = itemIndex / itemsPerPage
- * - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage]
+ Value(double value);
+ Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.)
+ Value(const char* begin, const char* end); ///< Copy all, incl zeroes.
+ /**
+ * \brief Constructs a value from a static string.
+ *
+ * Like other value string constructor but do not duplicate the string for
+ * internal storage. The given string must remain alive after the call to
+ * this constructor.
+ *
+ * \note This works only for null-terminated strings. (We cannot change the
+ * size of this class, so we have nowhere to store the length, which might be
+ * computed later for various operations.)
+ *
+ * Example of usage:
+ * \code
+ * static StaticString foo("some text");
+ * Json::Value aValue(foo);
+ * \endcode
+ */
+ Value(const StaticString& value);
+ Value(const String& value);
+ Value(bool value);
+ Value(std::nullptr_t ptr) = delete;
+ Value(const Value& other);
+ Value(Value&& other) noexcept;
+ ~Value();
+
+ /// \note Overwrite existing comments. To preserve comments, use
+ /// #swapPayload().
+ Value& operator=(const Value& other);
+ Value& operator=(Value&& other) noexcept;
+
+ /// Swap everything.
+ void swap(Value& other);
+ /// Swap values but leave comments and source offsets in place.
+ void swapPayload(Value& other);
+
+ /// copy everything.
+ void copy(const Value& other);
+ /// copy values but leave comments and source offsets in place.
+ void copyPayload(const Value& other);
+
+ ValueType type() const;
+
+ /// Compare payload only, not comments etc.
+ bool operator<(const Value& other) const;
+ bool operator<=(const Value& other) const;
+ bool operator>=(const Value& other) const;
+ bool operator>(const Value& other) const;
+ bool operator==(const Value& other) const;
+ bool operator!=(const Value& other) const;
+ int compare(const Value& other) const;
+
+ const char* asCString() const; ///< Embedded zeroes could cause you trouble!
+#if JSONCPP_USING_SECURE_MEMORY
+ unsigned getCStringLength() const; // Allows you to understand the length of
+ // the CString
+#endif
+ String asString() const; ///< Embedded zeroes are possible.
+ /** Get raw char* of string-value.
+ * \return false if !string. (Seg-fault if str or end are NULL.)
+ */
+ bool getString(char const** begin, char const** end) const;
+ Int asInt() const;
+ UInt asUInt() const;
+#if defined(JSON_HAS_INT64)
+ Int64 asInt64() const;
+ UInt64 asUInt64() const;
+#endif // if defined(JSON_HAS_INT64)
+ LargestInt asLargestInt() const;
+ LargestUInt asLargestUInt() const;
+ float asFloat() const;
+ double asDouble() const;
+ bool asBool() const;
+
+ bool isNull() const;
+ bool isBool() const;
+ bool isInt() const;
+ bool isInt64() const;
+ bool isUInt() const;
+ bool isUInt64() const;
+ bool isIntegral() const;
+ bool isDouble() const;
+ bool isNumeric() const;
+ bool isString() const;
+ bool isArray() const;
+ bool isObject() const;
+
+ /// The `as<T>` and `is<T>` member function templates and specializations.
+ template <typename T> T as() const JSONCPP_TEMPLATE_DELETE;
+ template <typename T> bool is() const JSONCPP_TEMPLATE_DELETE;
+
+ bool isConvertibleTo(ValueType other) const;
+
+ /// Number of values in array or object
+ ArrayIndex size() const;
+
+ /// \brief Return true if empty array, empty object, or null;
+ /// otherwise, false.
+ bool empty() const;
+
+ /// Return !isNull()
+ explicit operator bool() const;
+
+ /// Remove all object members and array elements.
+ /// \pre type() is arrayValue, objectValue, or nullValue
+ /// \post type() is unchanged
+ void clear();
+
+ /// Resize the array to newSize elements.
+ /// New elements are initialized to null.
+ /// May only be called on nullValue or arrayValue.
+ /// \pre type() is arrayValue or nullValue
+ /// \post type() is arrayValue
+ void resize(ArrayIndex newSize);
+
+ //@{
+ /// Access an array element (zero based index). If the array contains less
+ /// than index element, then null value are inserted in the array so that
+ /// its size is index+1.
+ /// (You may need to say 'value[0u]' to get your compiler to distinguish
+ /// this from the operator[] which takes a string.)
+ Value& operator[](ArrayIndex index);
+ Value& operator[](int index);
+ //@}
+
+ //@{
+ /// Access an array element (zero based index).
+ /// (You may need to say 'value[0u]' to get your compiler to distinguish
+ /// this from the operator[] which takes a string.)
+ const Value& operator[](ArrayIndex index) const;
+ const Value& operator[](int index) const;
+ //@}
+
+ /// If the array contains at least index+1 elements, returns the element
+ /// value, otherwise returns defaultValue.
+ Value get(ArrayIndex index, const Value& defaultValue) const;
+ /// Return true if index < size().
+ bool isValidIndex(ArrayIndex index) const;
+ /// \brief Append value to array at the end.
+ ///
+ /// Equivalent to jsonvalue[jsonvalue.size()] = value;
+ Value& append(const Value& value);
+ Value& append(Value&& value);
+
+ /// \brief Insert value in array at specific index
+ bool insert(ArrayIndex index, const Value& newValue);
+ bool insert(ArrayIndex index, Value&& newValue);
+
+ /// Access an object value by name, create a null member if it does not exist.
+ /// \note Because of our implementation, keys are limited to 2^30 -1 chars.
+ /// Exceeding that will cause an exception.
+ Value& operator[](const char* key);
+ /// Access an object value by name, returns null if there is no member with
+ /// that name.
+ const Value& operator[](const char* key) const;
+ /// Access an object value by name, create a null member if it does not exist.
+ /// \param key may contain embedded nulls.
+ Value& operator[](const String& key);
+ /// Access an object value by name, returns null if there is no member with
+ /// that name.
+ /// \param key may contain embedded nulls.
+ const Value& operator[](const String& key) const;
+ /** \brief Access an object value by name, create a null member if it does not
+ * exist.
+ *
+ * If the object has no entry for that name, then the member name used to
+ * store the new entry is not duplicated.
+ * Example of use:
+ * \code
+ * Json::Value object;
+ * static const StaticString code("code");
+ * object[code] = 1234;
+ * \endcode
+ */
+ Value& operator[](const StaticString& key);
+ /// Return the member named key if it exist, defaultValue otherwise.
+ /// \note deep copy
+ Value get(const char* key, const Value& defaultValue) const;
+ /// Return the member named key if it exist, defaultValue otherwise.
+ /// \note deep copy
+ /// \note key may contain embedded nulls.
+ Value get(const char* begin, const char* end,
+ const Value& defaultValue) const;
+ /// Return the member named key if it exist, defaultValue otherwise.
+ /// \note deep copy
+ /// \param key may contain embedded nulls.
+ Value get(const String& key, const Value& defaultValue) const;
+ /// Most general and efficient version of isMember()const, get()const,
+ /// and operator[]const
+ /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
+ Value const* find(char const* begin, char const* end) const;
+ /// Most general and efficient version of object-mutators.
+ /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
+ /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue.
+ Value* demand(char const* begin, char const* end);
+ /// \brief Remove and return the named member.
+ ///
+ /// Do nothing if it did not exist.
+ /// \pre type() is objectValue or nullValue
+ /// \post type() is unchanged
+ void removeMember(const char* key);
+ /// Same as removeMember(const char*)
+ /// \param key may contain embedded nulls.
+ void removeMember(const String& key);
+ /// Same as removeMember(const char* begin, const char* end, Value* removed),
+ /// but 'key' is null-terminated.
+ bool removeMember(const char* key, Value* removed);
+ /** \brief Remove the named map member.
+ *
+ * Update 'removed' iff removed.
+ * \param key may contain embedded nulls.
+ * \return true iff removed (no exceptions)
+ */
+ bool removeMember(String const& key, Value* removed);
+ /// Same as removeMember(String const& key, Value* removed)
+ bool removeMember(const char* begin, const char* end, Value* removed);
+ /** \brief Remove the indexed array element.
*
- * Insertion is amortized constant time (only the array containing the index of pointers
- * need to be reallocated when items are appended).
+ * O(n) expensive operations.
+ * Update 'removed' iff removed.
+ * \return true if removed (no exceptions)
*/
- class JSON_API ValueInternalArray
- {
- friend class Value;
- friend class ValueIteratorBase;
- public:
- enum { itemsPerPage = 8 }; // should be a power of 2 for fast divide and modulo.
- typedef Value::ArrayIndex ArrayIndex;
- typedef unsigned int PageIndex;
-
-# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
- struct IteratorState // Must be a POD
- {
- IteratorState()
- : array_(0)
- , currentPageIndex_(0)
- , currentItemIndex_(0)
- {
- }
- ValueInternalArray *array_;
- Value **currentPageIndex_;
- unsigned int currentItemIndex_;
- };
-# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
-
- ValueInternalArray();
- ValueInternalArray( const ValueInternalArray &other );
- ValueInternalArray &operator =( const ValueInternalArray &other );
- ~ValueInternalArray();
- void swap( ValueInternalArray &other );
-
- void clear();
- void resize( ArrayIndex newSize );
-
- Value &resolveReference( ArrayIndex index );
-
- Value *find( ArrayIndex index ) const;
-
- ArrayIndex size() const;
-
- int compare( const ValueInternalArray &other ) const;
-
- private:
- static bool equals( const IteratorState &x, const IteratorState &other );
- static void increment( IteratorState &iterator );
- static void decrement( IteratorState &iterator );
- static Value &dereference( const IteratorState &iterator );
- static Value &unsafeDereference( const IteratorState &iterator );
- static int distance( const IteratorState &x, const IteratorState &y );
- static ArrayIndex indexOf( const IteratorState &iterator );
- void makeBeginIterator( IteratorState &it ) const;
- void makeEndIterator( IteratorState &it ) const;
- void makeIterator( IteratorState &it, ArrayIndex index ) const;
-
- void makeIndexValid( ArrayIndex index );
-
- Value **pages_;
- ArrayIndex size_;
- PageIndex pageCount_;
- };
-
- /** \brief Experimental: do not use. Allocator to customize Value internal array.
- * Below is an example of a simple implementation (actual implementation use
- * memory pool).
- \code
-class DefaultValueArrayAllocator : public ValueArrayAllocator
-{
-public: // overridden from ValueArrayAllocator
- virtual ~DefaultValueArrayAllocator()
- {
- }
-
- virtual ValueInternalArray *newArray()
- {
- return new ValueInternalArray();
- }
-
- virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other )
- {
- return new ValueInternalArray( other );
- }
-
- virtual void destruct( ValueInternalArray *array )
- {
- delete array;
- }
-
- virtual void reallocateArrayPageIndex( Value **&indexes,
- ValueInternalArray::PageIndex &indexCount,
- ValueInternalArray::PageIndex minNewIndexCount )
- {
- ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1;
- if ( minNewIndexCount > newIndexCount )
- newIndexCount = minNewIndexCount;
- void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount );
- if ( !newIndexes )
- throw std::bad_alloc();
- indexCount = newIndexCount;
- indexes = static_cast<Value **>( newIndexes );
- }
- virtual void releaseArrayPageIndex( Value **indexes,
- ValueInternalArray::PageIndex indexCount )
- {
- if ( indexes )
- free( indexes );
- }
-
- virtual Value *allocateArrayPage()
- {
- return static_cast<Value *>( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) );
- }
-
- virtual void releaseArrayPage( Value *value )
- {
- if ( value )
- free( value );
- }
+ bool removeIndex(ArrayIndex index, Value* removed);
+
+ /// Return true if the object has a member named key.
+ /// \note 'key' must be null-terminated.
+ bool isMember(const char* key) const;
+ /// Return true if the object has a member named key.
+ /// \param key may contain embedded nulls.
+ bool isMember(const String& key) const;
+ /// Same as isMember(String const& key)const
+ bool isMember(const char* begin, const char* end) const;
+
+ /// \brief Return a list of the member names.
+ ///
+ /// If null, return an empty list.
+ /// \pre type() is objectValue or nullValue
+ /// \post if type() was nullValue, it remains nullValue
+ Members getMemberNames() const;
+
+ /// \deprecated Always pass len.
+ JSONCPP_DEPRECATED("Use setComment(String const&) instead.")
+ void setComment(const char* comment, CommentPlacement placement) {
+ setComment(String(comment, strlen(comment)), placement);
+ }
+ /// Comments must be //... or /* ... */
+ void setComment(const char* comment, size_t len, CommentPlacement placement) {
+ setComment(String(comment, len), placement);
+ }
+ /// Comments must be //... or /* ... */
+ void setComment(String comment, CommentPlacement placement);
+ bool hasComment(CommentPlacement placement) const;
+ /// Include delimiters and embedded newlines.
+ String getComment(CommentPlacement placement) const;
+
+ String toStyledString() const;
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ iterator begin();
+ iterator end();
+
+ // Accessors for the [start, limit) range of bytes within the JSON text from
+ // which this value was parsed, if any.
+ void setOffsetStart(ptrdiff_t start);
+ void setOffsetLimit(ptrdiff_t limit);
+ ptrdiff_t getOffsetStart() const;
+ ptrdiff_t getOffsetLimit() const;
+
+private:
+ void setType(ValueType v) {
+ bits_.value_type_ = static_cast<unsigned char>(v);
+ }
+ bool isAllocated() const { return bits_.allocated_; }
+ void setIsAllocated(bool v) { bits_.allocated_ = v; }
+
+ void initBasic(ValueType type, bool allocated = false);
+ void dupPayload(const Value& other);
+ void releasePayload();
+ void dupMeta(const Value& other);
+
+ Value& resolveReference(const char* key);
+ Value& resolveReference(const char* key, const char* end);
+
+ // struct MemberNamesTransform
+ //{
+ // typedef const char *result_type;
+ // const char *operator()( const CZString &name ) const
+ // {
+ // return name.c_str();
+ // }
+ //};
+
+ union ValueHolder {
+ LargestInt int_;
+ LargestUInt uint_;
+ double real_;
+ bool bool_;
+ char* string_; // if allocated_, ptr to { unsigned, char[] }.
+ ObjectValues* map_;
+ } value_;
+
+ struct {
+ // Really a ValueType, but types should agree for bitfield packing.
+ unsigned int value_type_ : 8;
+ // Unless allocated_, string_ must be null-terminated.
+ unsigned int allocated_ : 1;
+ } bits_;
+
+ class Comments {
+ public:
+ Comments() = default;
+ Comments(const Comments& that);
+ Comments(Comments&& that) noexcept;
+ Comments& operator=(const Comments& that);
+ Comments& operator=(Comments&& that) noexcept;
+ bool has(CommentPlacement slot) const;
+ String get(CommentPlacement slot) const;
+ void set(CommentPlacement slot, String comment);
+
+ private:
+ using Array = std::array<String, numberOfCommentPlacement>;
+ std::unique_ptr<Array> ptr_;
+ };
+ Comments comments_;
+
+ // [start, limit) byte offsets in the source JSON text from which this Value
+ // was extracted.
+ ptrdiff_t start_;
+ ptrdiff_t limit_;
};
- \endcode
- */
- class JSON_API ValueArrayAllocator
- {
- public:
- virtual ~ValueArrayAllocator();
- virtual ValueInternalArray *newArray() = 0;
- virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) = 0;
- virtual void destructArray( ValueInternalArray *array ) = 0;
- /** \brief Reallocate array page index.
- * Reallocates an array of pointer on each page.
- * \param indexes [input] pointer on the current index. May be \c nullptr.
- * [output] pointer on the new index of at least
- * \a minNewIndexCount pages.
- * \param indexCount [input] current number of pages in the index.
- * [output] number of page the reallocated index can handle.
- * \b MUST be >= \a minNewIndexCount.
- * \param minNewIndexCount Minimum number of page the new index must be able to
- * handle.
- */
- virtual void reallocateArrayPageIndex( Value **&indexes,
- ValueInternalArray::PageIndex &indexCount,
- ValueInternalArray::PageIndex minNewIndexCount ) = 0;
- virtual void releaseArrayPageIndex( Value **indexes,
- ValueInternalArray::PageIndex indexCount ) = 0;
- virtual Value *allocateArrayPage() = 0;
- virtual void releaseArrayPage( Value *value ) = 0;
- };
-#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP
-
-
- /** \brief base class for Value iterators.
- *
- */
- class ValueIteratorBase
- {
- public:
- typedef unsigned int size_t;
- typedef int difference_type;
- typedef ValueIteratorBase SelfType;
-
- ValueIteratorBase();
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- explicit ValueIteratorBase( const Value::ObjectValues::iterator &current );
-#else
- ValueIteratorBase( const ValueInternalArray::IteratorState &state );
- ValueIteratorBase( const ValueInternalMap::IteratorState &state );
+
+template <> inline bool Value::as<bool>() const { return asBool(); }
+template <> inline bool Value::is<bool>() const { return isBool(); }
+
+template <> inline Int Value::as<Int>() const { return asInt(); }
+template <> inline bool Value::is<Int>() const { return isInt(); }
+
+template <> inline UInt Value::as<UInt>() const { return asUInt(); }
+template <> inline bool Value::is<UInt>() const { return isUInt(); }
+
+#if defined(JSON_HAS_INT64)
+template <> inline Int64 Value::as<Int64>() const { return asInt64(); }
+template <> inline bool Value::is<Int64>() const { return isInt64(); }
+
+template <> inline UInt64 Value::as<UInt64>() const { return asUInt64(); }
+template <> inline bool Value::is<UInt64>() const { return isUInt64(); }
#endif
- bool operator ==( const SelfType &other ) const
- {
- return isEqual( other );
- }
+template <> inline double Value::as<double>() const { return asDouble(); }
+template <> inline bool Value::is<double>() const { return isDouble(); }
- bool operator !=( const SelfType &other ) const
- {
- return !isEqual( other );
- }
+template <> inline String Value::as<String>() const { return asString(); }
+template <> inline bool Value::is<String>() const { return isString(); }
- difference_type operator -( const SelfType &other ) const
- {
- return computeDistance( other );
- }
+/// These `as` specializations are type conversions, and do not have a
+/// corresponding `is`.
+template <> inline float Value::as<float>() const { return asFloat(); }
+template <> inline const char* Value::as<const char*>() const {
+ return asCString();
+}
- /// Return either the index or the member name of the referenced value as a Value.
- Value key() const;
+/** \brief Experimental and untested: represents an element of the "path" to
+ * access a node.
+ */
+class JSON_API PathArgument {
+public:
+ friend class Path;
+
+ PathArgument();
+ PathArgument(ArrayIndex index);
+ PathArgument(const char* key);
+ PathArgument(String key);
+
+private:
+ enum Kind { kindNone = 0, kindIndex, kindKey };
+ String key_;
+ ArrayIndex index_{};
+ Kind kind_{kindNone};
+};
- /// Return the index of the referenced Value. -1 if it is not an arrayValue.
- UInt index() const;
+/** \brief Experimental and untested: represents a "path" to access a node.
+ *
+ * Syntax:
+ * - "." => root node
+ * - ".[n]" => elements at index 'n' of root node (an array value)
+ * - ".name" => member named 'name' of root node (an object value)
+ * - ".name1.name2.name3"
+ * - ".[0][1][2].name1[3]"
+ * - ".%" => member name is provided as parameter
+ * - ".[%]" => index is provided as parameter
+ */
+class JSON_API Path {
+public:
+ Path(const String& path, const PathArgument& a1 = PathArgument(),
+ const PathArgument& a2 = PathArgument(),
+ const PathArgument& a3 = PathArgument(),
+ const PathArgument& a4 = PathArgument(),
+ const PathArgument& a5 = PathArgument());
+
+ const Value& resolve(const Value& root) const;
+ Value resolve(const Value& root, const Value& defaultValue) const;
+ /// Creates the "path" to access the specified node and returns a reference on
+ /// the node.
+ Value& make(Value& root) const;
+
+private:
+ using InArgs = std::vector<const PathArgument*>;
+ using Args = std::vector<PathArgument>;
+
+ void makePath(const String& path, const InArgs& in);
+ void addPathInArg(const String& path, const InArgs& in,
+ InArgs::const_iterator& itInArg, PathArgument::Kind kind);
+ static void invalidPath(const String& path, int location);
+
+ Args args_;
+};
- /// Return the member name of the referenced Value. "" if it is not an objectValue.
- const char *memberName() const;
+/** \brief base class for Value iterators.
+ *
+ */
+class JSON_API ValueIteratorBase {
+public:
+ using iterator_category = std::bidirectional_iterator_tag;
+ using size_t = unsigned int;
+ using difference_type = int;
+ using SelfType = ValueIteratorBase;
+
+ bool operator==(const SelfType& other) const { return isEqual(other); }
+
+ bool operator!=(const SelfType& other) const { return !isEqual(other); }
+
+ difference_type operator-(const SelfType& other) const {
+ return other.computeDistance(*this);
+ }
+
+ /// Return either the index or the member name of the referenced value as a
+ /// Value.
+ Value key() const;
+
+ /// Return the index of the referenced Value, or -1 if it is not an
+ /// arrayValue.
+ UInt index() const;
+
+ /// Return the member name of the referenced Value, or "" if it is not an
+ /// objectValue.
+ /// \note Avoid `c_str()` on result, as embedded zeroes are possible.
+ String name() const;
+
+ /// Return the member name of the referenced Value. "" if it is not an
+ /// objectValue.
+ /// \deprecated This cannot be used for UTF-8 strings, since there can be
+ /// embedded nulls.
+ JSONCPP_DEPRECATED("Use `key = name();` instead.")
+ char const* memberName() const;
+ /// Return the member name of the referenced Value, or NULL if it is not an
+ /// objectValue.
+ /// \note Better version than memberName(). Allows embedded nulls.
+ char const* memberName(char const** end) const;
+
+protected:
+ /*! Internal utility functions to assist with implementing
+ * other iterator functions. The const and non-const versions
+ * of the "deref" protected methods expose the protected
+ * current_ member variable in a way that can often be
+ * optimized away by the compiler.
+ */
+ const Value& deref() const;
+ Value& deref();
- protected:
- Value &deref() const;
+ void increment();
- void increment();
+ void decrement();
- void decrement();
+ difference_type computeDistance(const SelfType& other) const;
- difference_type computeDistance( const SelfType &other ) const;
+ bool isEqual(const SelfType& other) const;
- bool isEqual( const SelfType &other ) const;
+ void copy(const SelfType& other);
- void copy( const SelfType &other );
+private:
+ Value::ObjectValues::iterator current_;
+ // Indicates that iterator is for a null value.
+ bool isNull_{true};
- private:
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- Value::ObjectValues::iterator current_;
- // Indicates that iterator is for a null value.
- bool isNull_;
-#else
- union
- {
- ValueInternalArray::IteratorState array_;
- ValueInternalMap::IteratorState map_;
- } iterator_;
- bool isArray_;
-#endif
- };
-
- /** \brief const iterator for object and array value.
- *
- */
- class ValueConstIterator : public ValueIteratorBase
- {
- friend class Value;
- public:
- typedef unsigned int size_t;
- typedef int difference_type;
- typedef const Value &reference;
- typedef const Value *pointer;
- typedef ValueConstIterator SelfType;
-
- ValueConstIterator();
- private:
- /*! \internal Use by Value to create an iterator.
- */
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- explicit ValueConstIterator( const Value::ObjectValues::iterator &current );
-#else
- ValueConstIterator( const ValueInternalArray::IteratorState &state );
- ValueConstIterator( const ValueInternalMap::IteratorState &state );
-#endif
- public:
- SelfType &operator =( const ValueIteratorBase &other );
-
- SelfType operator++( int )
- {
- SelfType temp( *this );
- ++*this;
- return temp;
- }
-
- SelfType operator--( int )
- {
- SelfType temp( *this );
- --*this;
- return temp;
- }
-
- SelfType &operator--()
- {
- decrement();
- return *this;
- }
-
- SelfType &operator++()
- {
- increment();
- return *this;
- }
-
- reference operator *() const
- {
- return deref();
- }
- };
-
-
- /** \brief Iterator for object and array value.
- */
- class ValueIterator : public ValueIteratorBase
- {
- friend class Value;
- public:
- typedef unsigned int size_t;
- typedef int difference_type;
- typedef Value &reference;
- typedef Value *pointer;
- typedef ValueIterator SelfType;
-
- ValueIterator();
- ValueIterator( const ValueConstIterator &other );
- ValueIterator( const ValueIterator &other );
- private:
- /*! \internal Use by Value to create an iterator.
- */
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
- explicit ValueIterator( const Value::ObjectValues::iterator &current );
-#else
- ValueIterator( const ValueInternalArray::IteratorState &state );
- ValueIterator( const ValueInternalMap::IteratorState &state );
-#endif
- public:
-
- SelfType &operator =( const SelfType &other );
-
- SelfType operator++( int )
- {
- SelfType temp( *this );
- ++*this;
- return temp;
- }
-
- SelfType operator--( int )
- {
- SelfType temp( *this );
- --*this;
- return temp;
- }
-
- SelfType &operator--()
- {
- decrement();
- return *this;
- }
-
- SelfType &operator++()
- {
- increment();
- return *this;
- }
-
- reference operator *() const
- {
- return deref();
- }
- };
+public:
+ // For some reason, BORLAND needs these at the end, rather
+ // than earlier. No idea why.
+ ValueIteratorBase();
+ explicit ValueIteratorBase(const Value::ObjectValues::iterator& current);
+};
+
+/** \brief const iterator for object and array value.
+ *
+ */
+class JSON_API ValueConstIterator : public ValueIteratorBase {
+ friend class Value;
+
+public:
+ using value_type = const Value;
+ // typedef unsigned int size_t;
+ // typedef int difference_type;
+ using reference = const Value&;
+ using pointer = const Value*;
+ using SelfType = ValueConstIterator;
+
+ ValueConstIterator();
+ ValueConstIterator(ValueIterator const& other);
+
+private:
+ /*! \internal Use by Value to create an iterator.
+ */
+ explicit ValueConstIterator(const Value::ObjectValues::iterator& current);
+
+public:
+ SelfType& operator=(const ValueIteratorBase& other);
+
+ SelfType operator++(int) {
+ SelfType temp(*this);
+ ++*this;
+ return temp;
+ }
+
+ SelfType operator--(int) {
+ SelfType temp(*this);
+ --*this;
+ return temp;
+ }
+ SelfType& operator--() {
+ decrement();
+ return *this;
+ }
+
+ SelfType& operator++() {
+ increment();
+ return *this;
+ }
+
+ reference operator*() const { return deref(); }
+
+ pointer operator->() const { return &deref(); }
+};
+
+/** \brief Iterator for object and array value.
+ */
+class JSON_API ValueIterator : public ValueIteratorBase {
+ friend class Value;
+
+public:
+ using value_type = Value;
+ using size_t = unsigned int;
+ using difference_type = int;
+ using reference = Value&;
+ using pointer = Value*;
+ using SelfType = ValueIterator;
+
+ ValueIterator();
+ explicit ValueIterator(const ValueConstIterator& other);
+ ValueIterator(const ValueIterator& other);
+
+private:
+ /*! \internal Use by Value to create an iterator.
+ */
+ explicit ValueIterator(const Value::ObjectValues::iterator& current);
+
+public:
+ SelfType& operator=(const SelfType& other);
+
+ SelfType operator++(int) {
+ SelfType temp(*this);
+ ++*this;
+ return temp;
+ }
+
+ SelfType operator--(int) {
+ SelfType temp(*this);
+ --*this;
+ return temp;
+ }
+
+ SelfType& operator--() {
+ decrement();
+ return *this;
+ }
+
+ SelfType& operator++() {
+ increment();
+ return *this;
+ }
+
+ /*! The return value of non-const iterators can be
+ * changed, so the these functions are not const
+ * because the returned references/pointers can be used
+ * to change state of the base class.
+ */
+ reference operator*() const { return const_cast<reference>(deref()); }
+ pointer operator->() const { return const_cast<pointer>(&deref()); }
+};
+
+inline void swap(Value& a, Value& b) { a.swap(b); }
} // namespace Json
+#pragma pack(pop)
-#endif // CPPTL_JSON_H_INCLUDED
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(pop)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#endif // JSON_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
// End of content of file: include/json/value.h
// //////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: include/json/reader.h
// //////////////////////////////////////////////////////////////////////
-// Copyright 2007-2010 Baptiste Lepilleur
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-#ifndef CPPTL_JSON_READER_H_INCLUDED
-# define CPPTL_JSON_READER_H_INCLUDED
+#ifndef JSON_READER_H_INCLUDED
+#define JSON_READER_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
-# include "features.h"
-# include "value.h"
+#include "json_features.h"
+#include "value.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
-# include <deque>
-# include <stack>
-# include <string>
-# include <iostream>
+#include <deque>
+#include <iosfwd>
+#include <istream>
+#include <stack>
+#include <string>
+
+// Disable warning C4251: <data member>: <type> needs to have dll-interface to
+// be used by...
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(push)
+#pragma warning(disable : 4251)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#pragma pack(push, 8)
namespace Json {
- /** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a Value.
- *
- */
- class JSON_API Reader
- {
- public:
- typedef char Char;
- typedef const Char *Location;
-
- /** \brief Constructs a Reader allowing all features
- * for parsing.
- */
- Reader();
-
- /** \brief Constructs a Reader allowing the specified feature set
- * for parsing.
- */
- Reader( const Features &features );
-
- /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> document.
- * \param document UTF-8 encoded string containing the document to read.
- * \param root [out] Contains the root value of the document if it was
- * successfully parsed.
- * \param collectComments \c true to collect comment and allow writing them back during
- * serialization, \c false to discard comments.
- * This parameter is ignored if Features::allowComments_
- * is \c false.
- * \return \c true if the document was successfully parsed, \c false if an error occurred.
- */
- bool parse( const std::string &document,
- Value &root,
- bool collectComments = true );
-
- /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> document.
- * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the document to read.
- * \param endDoc Pointer on the end of the UTF-8 encoded string of the document to read.
- \ Must be >= beginDoc.
- * \param root [out] Contains the root value of the document if it was
- * successfully parsed.
- * \param collectComments \c true to collect comment and allow writing them back during
- * serialization, \c false to discard comments.
- * This parameter is ignored if Features::allowComments_
- * is \c false.
- * \return \c true if the document was successfully parsed, \c false if an error occurred.
- */
- bool parse( const char *beginDoc, const char *endDoc,
- Value &root,
- bool collectComments = true );
-
- /// \brief Parse from input stream.
- /// \see Json::operator>>(std::istream&, Json::Value&).
- bool parse( std::istream &is,
- Value &root,
- bool collectComments = true );
-
- /** \brief Returns a user friendly string that list errors in the parsed document.
- * \return Formatted error message with the list of errors with their location in
- * the parsed document. An empty string is returned if no error occurred
- * during parsing.
- * \deprecated Use getFormattedErrorMessages() instead (typo fix).
- */
- JSONCPP_DEPRECATED("Use getFormattedErrorMessages instead")
- std::string getFormatedErrorMessages() const;
-
- /** \brief Returns a user friendly string that list errors in the parsed document.
- * \return Formatted error message with the list of errors with their location in
- * the parsed document. An empty string is returned if no error occurred
- * during parsing.
- */
- std::string getFormattedErrorMessages() const;
-
- private:
- enum TokenType
- {
- tokenEndOfStream = 0,
- tokenObjectBegin,
- tokenObjectEnd,
- tokenArrayBegin,
- tokenArrayEnd,
- tokenString,
- tokenNumber,
- tokenTrue,
- tokenFalse,
- tokenNull,
- tokenArraySeparator,
- tokenMemberSeparator,
- tokenComment,
- tokenError
- };
-
- class Token
- {
- public:
- TokenType type_;
- Location start_;
- Location end_;
- };
-
- class ErrorInfo
- {
- public:
- Token token_;
- std::string message_;
- Location extra_;
- };
-
- typedef std::deque<ErrorInfo> Errors;
-
- bool expectToken( TokenType type, Token &token, const char *message );
- bool readToken( Token &token );
- void skipSpaces();
- bool match( Location pattern,
- int patternLength );
- bool readComment();
- bool readCStyleComment();
- bool readCppStyleComment();
- bool readString();
- void readNumber();
- bool readValue();
- bool readObject( Token &token );
- bool readArray( Token &token );
- bool decodeNumber( Token &token );
- bool decodeString( Token &token );
- bool decodeString( Token &token, std::string &decoded );
- bool decodeDouble( Token &token );
- bool decodeUnicodeCodePoint( Token &token,
- Location &current,
- Location end,
- unsigned int &unicode );
- bool decodeUnicodeEscapeSequence( Token &token,
- Location &current,
- Location end,
- unsigned int &unicode );
- bool addError( const std::string &message,
- Token &token,
- Location extra = nullptr );
- bool recoverFromError( TokenType skipUntilToken );
- bool addErrorAndRecover( const std::string &message,
- Token &token,
- TokenType skipUntilToken );
- void skipUntilSpace();
- Value &currentValue();
- Char getNextChar();
- void getLocationLineAndColumn( Location location,
- int &line,
- int &column ) const;
- std::string getLocationLineAndColumn( Location location ) const;
- void addComment( Location begin,
- Location end,
- CommentPlacement placement );
- void skipCommentTokens( Token &token );
-
- typedef std::stack<Value *> Nodes;
- Nodes nodes_;
- Errors errors_;
- std::string document_;
- Location begin_;
- Location end_;
- Location current_;
- Location lastValueEnd_;
- Value *lastValue_;
- std::string commentsBefore_;
- Features features_;
- bool collectComments_;
- };
-
- /** \brief Read from 'sin' into 'root'.
-
- Always keep comments from the input JSON.
-
- This can be used to read a file into a particular sub-object.
- For example:
- \code
- Json::Value root;
- cin >> root["dir"]["file"];
- cout << root;
- \endcode
- Result:
- \verbatim
- {
- "dir": {
- "file": {
- // The input stream JSON would be nested here.
- }
- }
- }
- \endverbatim
- \throw std::exception on parse error.
- \see Json::operator<<()
+/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a
+ * Value.
+ *
+ * \deprecated Use CharReader and CharReaderBuilder.
+ */
+
+class JSON_API Reader {
+public:
+ using Char = char;
+ using Location = const Char*;
+
+ /** \brief An error tagged with where in the JSON text it was encountered.
+ *
+ * The offsets give the [start, limit) range of bytes within the text. Note
+ * that this is bytes, not codepoints.
+ */
+ struct StructuredError {
+ ptrdiff_t offset_start;
+ ptrdiff_t offset_limit;
+ String message;
+ };
+
+ /** \brief Constructs a Reader allowing all features for parsing.
+ * \deprecated Use CharReader and CharReaderBuilder.
+ */
+ Reader();
+
+ /** \brief Constructs a Reader allowing the specified feature set for parsing.
+ * \deprecated Use CharReader and CharReaderBuilder.
*/
- std::istream& operator>>( std::istream&, Value& );
+ Reader(const Features& features);
+
+ /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
+ * document.
+ *
+ * \param document UTF-8 encoded string containing the document
+ * to read.
+ * \param[out] root Contains the root value of the document if it
+ * was successfully parsed.
+ * \param collectComments \c true to collect comment and allow writing
+ * them back during serialization, \c false to
+ * discard comments. This parameter is ignored
+ * if Features::allowComments_ is \c false.
+ * \return \c true if the document was successfully parsed, \c false if an
+ * error occurred.
+ */
+ bool parse(const std::string& document, Value& root,
+ bool collectComments = true);
+
+ /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
+ * document.
+ *
+ * \param beginDoc Pointer on the beginning of the UTF-8 encoded
+ * string of the document to read.
+ * \param endDoc Pointer on the end of the UTF-8 encoded string
+ * of the document to read. Must be >= beginDoc.
+ * \param[out] root Contains the root value of the document if it
+ * was successfully parsed.
+ * \param collectComments \c true to collect comment and allow writing
+ * them back during serialization, \c false to
+ * discard comments. This parameter is ignored
+ * if Features::allowComments_ is \c false.
+ * \return \c true if the document was successfully parsed, \c false if an
+ * error occurred.
+ */
+ bool parse(const char* beginDoc, const char* endDoc, Value& root,
+ bool collectComments = true);
+
+ /// \brief Parse from input stream.
+ /// \see Json::operator>>(std::istream&, Json::Value&).
+ bool parse(IStream& is, Value& root, bool collectComments = true);
+
+ /** \brief Returns a user friendly string that list errors in the parsed
+ * document.
+ *
+ * \return Formatted error message with the list of errors with their
+ * location in the parsed document. An empty string is returned if no error
+ * occurred during parsing.
+ * \deprecated Use getFormattedErrorMessages() instead (typo fix).
+ */
+ JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.")
+ String getFormatedErrorMessages() const;
+
+ /** \brief Returns a user friendly string that list errors in the parsed
+ * document.
+ *
+ * \return Formatted error message with the list of errors with their
+ * location in the parsed document. An empty string is returned if no error
+ * occurred during parsing.
+ */
+ String getFormattedErrorMessages() const;
+
+ /** \brief Returns a vector of structured errors encountered while parsing.
+ *
+ * \return A (possibly empty) vector of StructuredError objects. Currently
+ * only one error can be returned, but the caller should tolerate multiple
+ * errors. This can occur if the parser recovers from a non-fatal parse
+ * error and then encounters additional errors.
+ */
+ std::vector<StructuredError> getStructuredErrors() const;
+
+ /** \brief Add a semantic error message.
+ *
+ * \param value JSON Value location associated with the error
+ * \param message The error message.
+ * \return \c true if the error was successfully added, \c false if the Value
+ * offset exceeds the document size.
+ */
+ bool pushError(const Value& value, const String& message);
+
+ /** \brief Add a semantic error message with extra context.
+ *
+ * \param value JSON Value location associated with the error
+ * \param message The error message.
+ * \param extra Additional JSON Value location to contextualize the error
+ * \return \c true if the error was successfully added, \c false if either
+ * Value offset exceeds the document size.
+ */
+ bool pushError(const Value& value, const String& message, const Value& extra);
+
+ /** \brief Return whether there are any errors.
+ *
+ * \return \c true if there are no errors to report \c false if errors have
+ * occurred.
+ */
+ bool good() const;
+
+private:
+ enum TokenType {
+ tokenEndOfStream = 0,
+ tokenObjectBegin,
+ tokenObjectEnd,
+ tokenArrayBegin,
+ tokenArrayEnd,
+ tokenString,
+ tokenNumber,
+ tokenTrue,
+ tokenFalse,
+ tokenNull,
+ tokenArraySeparator,
+ tokenMemberSeparator,
+ tokenComment,
+ tokenError
+ };
+
+ class Token {
+ public:
+ TokenType type_;
+ Location start_;
+ Location end_;
+ };
+
+ class ErrorInfo {
+ public:
+ Token token_;
+ String message_;
+ Location extra_;
+ };
+
+ using Errors = std::deque<ErrorInfo>;
+
+ bool readToken(Token& token);
+ void skipSpaces();
+ bool match(const Char* pattern, int patternLength);
+ bool readComment();
+ bool readCStyleComment();
+ bool readCppStyleComment();
+ bool readString();
+ void readNumber();
+ bool readValue();
+ bool readObject(Token& token);
+ bool readArray(Token& token);
+ bool decodeNumber(Token& token);
+ bool decodeNumber(Token& token, Value& decoded);
+ bool decodeString(Token& token);
+ bool decodeString(Token& token, String& decoded);
+ bool decodeDouble(Token& token);
+ bool decodeDouble(Token& token, Value& decoded);
+ bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
+ unsigned int& unicode);
+ bool decodeUnicodeEscapeSequence(Token& token, Location& current,
+ Location end, unsigned int& unicode);
+ bool addError(const String& message, Token& token, Location extra = nullptr);
+ bool recoverFromError(TokenType skipUntilToken);
+ bool addErrorAndRecover(const String& message, Token& token,
+ TokenType skipUntilToken);
+ void skipUntilSpace();
+ Value& currentValue();
+ Char getNextChar();
+ void getLocationLineAndColumn(Location location, int& line,
+ int& column) const;
+ String getLocationLineAndColumn(Location location) const;
+ void addComment(Location begin, Location end, CommentPlacement placement);
+ void skipCommentTokens(Token& token);
+
+ static bool containsNewLine(Location begin, Location end);
+ static String normalizeEOL(Location begin, Location end);
+
+ using Nodes = std::stack<Value*>;
+ Nodes nodes_;
+ Errors errors_;
+ String document_;
+ Location begin_{};
+ Location end_{};
+ Location current_{};
+ Location lastValueEnd_{};
+ Value* lastValue_{};
+ String commentsBefore_;
+ Features features_;
+ bool collectComments_{};
+}; // Reader
+
+/** Interface for reading JSON from a char array.
+ */
+class JSON_API CharReader {
+public:
+ virtual ~CharReader() = default;
+ /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
+ * document. The document must be a UTF-8 encoded string containing the
+ * document to read.
+ *
+ * \param beginDoc Pointer on the beginning of the UTF-8 encoded string
+ * of the document to read.
+ * \param endDoc Pointer on the end of the UTF-8 encoded string of the
+ * document to read. Must be >= beginDoc.
+ * \param[out] root Contains the root value of the document if it was
+ * successfully parsed.
+ * \param[out] errs Formatted error messages (if not NULL) a user
+ * friendly string that lists errors in the parsed
+ * document.
+ * \return \c true if the document was successfully parsed, \c false if an
+ * error occurred.
+ */
+ virtual bool parse(char const* beginDoc, char const* endDoc, Value* root,
+ String* errs) = 0;
+
+ class JSON_API Factory {
+ public:
+ virtual ~Factory() = default;
+ /** \brief Allocate a CharReader via operator new().
+ * \throw std::exception if something goes wrong (e.g. invalid settings)
+ */
+ virtual CharReader* newCharReader() const = 0;
+ }; // Factory
+}; // CharReader
+
+/** \brief Build a CharReader implementation.
+ *
+ * Usage:
+ * \code
+ * using namespace Json;
+ * CharReaderBuilder builder;
+ * builder["collectComments"] = false;
+ * Value value;
+ * String errs;
+ * bool ok = parseFromStream(builder, std::cin, &value, &errs);
+ * \endcode
+ */
+class JSON_API CharReaderBuilder : public CharReader::Factory {
+public:
+ // Note: We use a Json::Value so that we can add data-members to this class
+ // without a major version bump.
+ /** Configuration of this builder.
+ * These are case-sensitive.
+ * Available settings (case-sensitive):
+ * - `"collectComments": false or true`
+ * - true to collect comment and allow writing them back during
+ * serialization, false to discard comments. This parameter is ignored
+ * if allowComments is false.
+ * - `"allowComments": false or true`
+ * - true if comments are allowed.
+ * - `"allowTrailingCommas": false or true`
+ * - true if trailing commas in objects and arrays are allowed.
+ * - `"strictRoot": false or true`
+ * - true if root must be either an array or an object value
+ * - `"allowDroppedNullPlaceholders": false or true`
+ * - true if dropped null placeholders are allowed. (See
+ * StreamWriterBuilder.)
+ * - `"allowNumericKeys": false or true`
+ * - true if numeric object keys are allowed.
+ * - `"allowSingleQuotes": false or true`
+ * - true if '' are allowed for strings (both keys and values)
+ * - `"stackLimit": integer`
+ * - Exceeding stackLimit (recursive depth of `readValue()`) will cause an
+ * exception.
+ * - This is a security issue (seg-faults caused by deeply nested JSON), so
+ * the default is low.
+ * - `"failIfExtra": false or true`
+ * - If true, `parse()` returns false when extra non-whitespace trails the
+ * JSON value in the input string.
+ * - `"rejectDupKeys": false or true`
+ * - If true, `parse()` returns false when a key is duplicated within an
+ * object.
+ * - `"allowSpecialFloats": false or true`
+ * - If true, special float values (NaNs and infinities) are allowed and
+ * their values are lossfree restorable.
+ * - `"skipBom": false or true`
+ * - If true, if the input starts with the Unicode byte order mark (BOM),
+ * it is skipped.
+ *
+ * You can examine 'settings_` yourself to see the defaults. You can also
+ * write and read them just like any JSON Value.
+ * \sa setDefaults()
+ */
+ Json::Value settings_;
+
+ CharReaderBuilder();
+ ~CharReaderBuilder() override;
+
+ CharReader* newCharReader() const override;
+
+ /** \return true if 'settings' are legal and consistent;
+ * otherwise, indicate bad settings via 'invalid'.
+ */
+ bool validate(Json::Value* invalid) const;
+
+ /** A simple way to update a specific setting.
+ */
+ Value& operator[](const String& key);
+
+ /** Called by ctor, but you can use this to reset settings_.
+ * \pre 'settings' != NULL (but Json::null is fine)
+ * \remark Defaults:
+ * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults
+ */
+ static void setDefaults(Json::Value* settings);
+ /** Same as old Features::strictMode().
+ * \pre 'settings' != NULL (but Json::null is fine)
+ * \remark Defaults:
+ * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode
+ */
+ static void strictMode(Json::Value* settings);
+};
+
+/** Consume entire stream and use its begin/end.
+ * Someday we might have a real StreamReader, but for now this
+ * is convenient.
+ */
+bool JSON_API parseFromStream(CharReader::Factory const&, IStream&, Value* root,
+ String* errs);
+
+/** \brief Read from 'sin' into 'root'.
+ *
+ * Always keep comments from the input JSON.
+ *
+ * This can be used to read a file into a particular sub-object.
+ * For example:
+ * \code
+ * Json::Value root;
+ * cin >> root["dir"]["file"];
+ * cout << root;
+ * \endcode
+ * Result:
+ * \verbatim
+ * {
+ * "dir": {
+ * "file": {
+ * // The input stream JSON would be nested here.
+ * }
+ * }
+ * }
+ * \endverbatim
+ * \throw std::exception on parse error.
+ * \see Json::operator<<()
+ */
+JSON_API IStream& operator>>(IStream&, Value&);
} // namespace Json
-#endif // CPPTL_JSON_READER_H_INCLUDED
+#pragma pack(pop)
+
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(pop)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#endif // JSON_READER_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
// End of content of file: include/json/reader.h
// //////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: include/json/writer.h
// //////////////////////////////////////////////////////////////////////
-// Copyright 2007-2010 Baptiste Lepilleur
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_WRITER_H_INCLUDED
-# define JSON_WRITER_H_INCLUDED
+#define JSON_WRITER_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
-# include "value.h"
+#include "value.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
-# include <vector>
-# include <string>
-# include <iostream>
+#include <ostream>
+#include <string>
+#include <vector>
+
+// Disable warning C4251: <data member>: <type> needs to have dll-interface to
+// be used by...
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) && defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4251)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#pragma pack(push, 8)
namespace Json {
- class Value;
-
- /** \brief Abstract class for writers.
- */
- class JSON_API Writer
- {
- public:
- virtual ~Writer();
-
- virtual std::string write( const Value &root ) = 0;
- };
-
- /** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format without formatting (not human friendly).
- *
- * The JSON document is written in a single line. It is not intended for 'human' consumption,
- * but may be usefull to support feature such as RPC where bandwith is limited.
- * \sa Reader, Value
- */
- class JSON_API FastWriter : public Writer
- {
- public:
- FastWriter();
- ~FastWriter() override{}
-
- void enableYAMLCompatibility();
-
- public: // overridden from Writer
- std::string write( const Value &root ) override;
-
- private:
- void writeValue( const Value &value );
-
- std::string document_;
- bool yamlCompatiblityEnabled_;
- };
-
- /** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way.
- *
- * The rules for line break and indent are as follow:
- * - Object value:
- * - if empty then print {} without indent and line break
- * - if not empty the print '{', line break & indent, print one value per line
- * and then unindent and line break and print '}'.
- * - Array value:
- * - if empty then print [] without indent and line break
- * - if the array contains no object value, empty array or some other value types,
- * and all the values fit on one lines, then print the array on a single line.
- * - otherwise, it the values do not fit on one line, or the array contains
- * object or non empty array, then print one value per line.
- *
- * If the Value have comments then they are outputed according to their #CommentPlacement.
- *
- * \sa Reader, Value, Value::setComment()
- */
- class JSON_API StyledWriter: public Writer
- {
- public:
- StyledWriter();
- ~StyledWriter() override{}
-
- public: // overridden from Writer
- /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
- * \param root Value to serialize.
- * \return String containing the JSON document that represents the root value.
- */
- std::string write( const Value &root ) override;
-
- private:
- void writeValue( const Value &value );
- void writeArrayValue( const Value &value );
- bool isMultineArray( const Value &value );
- void pushValue( const std::string &value );
- void writeIndent();
- void writeWithIndent( const std::string &value );
- void indent();
- void unindent();
- void writeCommentBeforeValue( const Value &root );
- void writeCommentAfterValueOnSameLine( const Value &root );
- bool hasCommentForValue( const Value &value );
- static std::string normalizeEOL( const std::string &text );
-
- typedef std::vector<std::string> ChildValues;
-
- ChildValues childValues_;
- std::string document_;
- std::string indentString_;
- int rightMargin_;
- int indentSize_;
- bool addChildValues_;
- };
-
- /** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way,
- to a stream rather than to a string.
- *
- * The rules for line break and indent are as follow:
- * - Object value:
- * - if empty then print {} without indent and line break
- * - if not empty the print '{', line break & indent, print one value per line
- * and then unindent and line break and print '}'.
- * - Array value:
- * - if empty then print [] without indent and line break
- * - if the array contains no object value, empty array or some other value types,
- * and all the values fit on one lines, then print the array on a single line.
- * - otherwise, it the values do not fit on one line, or the array contains
- * object or non empty array, then print one value per line.
- *
- * If the Value have comments then they are outputed according to their #CommentPlacement.
- *
- * \param indentation Each level will be indented by this amount extra.
- * \sa Reader, Value, Value::setComment()
- */
- class JSON_API StyledStreamWriter
- {
- public:
- StyledStreamWriter( std::string indentation="\t" );
- ~StyledStreamWriter(){}
-
- public:
- /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
- * \param out Stream to write to. (Can be ostringstream, e.g.)
- * \param root Value to serialize.
- * \note There is no point in deriving from Writer, since write() should not return a value.
- */
- void write( std::ostream &out, const Value &root );
-
- private:
- void writeValue( const Value &value );
- void writeArrayValue( const Value &value );
- bool isMultineArray( const Value &value );
- void pushValue( const std::string &value );
- void writeIndent();
- void writeWithIndent( const std::string &value );
- void indent();
- void unindent();
- void writeCommentBeforeValue( const Value &root );
- void writeCommentAfterValueOnSameLine( const Value &root );
- bool hasCommentForValue( const Value &value );
- static std::string normalizeEOL( const std::string &text );
-
- typedef std::vector<std::string> ChildValues;
-
- ChildValues childValues_;
- std::ostream* document_;
- std::string indentString_;
- int rightMargin_;
- std::string indentation_;
- bool addChildValues_;
- };
-
-# if defined(JSON_HAS_INT64)
- std::string JSON_API valueToString( Int value );
- std::string JSON_API valueToString( UInt value );
-# endif // if defined(JSON_HAS_INT64)
- std::string JSON_API valueToString( LargestInt value );
- std::string JSON_API valueToString( LargestUInt value );
- std::string JSON_API valueToString( double value );
- std::string JSON_API valueToString( bool value );
- std::string JSON_API valueToQuotedString( const char *value );
-
- /// \brief Output using the StyledStreamWriter.
- /// \see Json::operator>>()
- std::ostream& operator<<( std::ostream&, const Value &root );
+class Value;
+
+/**
+ *
+ * Usage:
+ * \code
+ * using namespace Json;
+ * void writeToStdout(StreamWriter::Factory const& factory, Value const& value)
+ * { std::unique_ptr<StreamWriter> const writer( factory.newStreamWriter());
+ * writer->write(value, &std::cout);
+ * std::cout << std::endl; // add lf and flush
+ * }
+ * \endcode
+ */
+class JSON_API StreamWriter {
+protected:
+ OStream* sout_; // not owned; will not delete
+public:
+ StreamWriter();
+ virtual ~StreamWriter();
+ /** Write Value into document as configured in sub-class.
+ * Do not take ownership of sout, but maintain a reference during function.
+ * \pre sout != NULL
+ * \return zero on success (For now, we always return zero, so check the
+ * stream instead.) \throw std::exception possibly, depending on
+ * configuration
+ */
+ virtual int write(Value const& root, OStream* sout) = 0;
+
+ /** \brief A simple abstract factory.
+ */
+ class JSON_API Factory {
+ public:
+ virtual ~Factory();
+ /** \brief Allocate a CharReader via operator new().
+ * \throw std::exception if something goes wrong (e.g. invalid settings)
+ */
+ virtual StreamWriter* newStreamWriter() const = 0;
+ }; // Factory
+}; // StreamWriter
+
+/** \brief Write into stringstream, then return string, for convenience.
+ * A StreamWriter will be created from the factory, used, and then deleted.
+ */
+String JSON_API writeString(StreamWriter::Factory const& factory,
+ Value const& root);
+
+/** \brief Build a StreamWriter implementation.
+
+* Usage:
+* \code
+* using namespace Json;
+* Value value = ...;
+* StreamWriterBuilder builder;
+* builder["commentStyle"] = "None";
+* builder["indentation"] = " "; // or whatever you like
+* std::unique_ptr<Json::StreamWriter> writer(
+* builder.newStreamWriter());
+* writer->write(value, &std::cout);
+* std::cout << std::endl; // add lf and flush
+* \endcode
+*/
+class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
+public:
+ // Note: We use a Json::Value so that we can add data-members to this class
+ // without a major version bump.
+ /** Configuration of this builder.
+ * Available settings (case-sensitive):
+ * - "commentStyle": "None" or "All"
+ * - "indentation": "<anything>".
+ * - Setting this to an empty string also omits newline characters.
+ * - "enableYAMLCompatibility": false or true
+ * - slightly change the whitespace around colons
+ * - "dropNullPlaceholders": false or true
+ * - Drop the "null" string from the writer's output for nullValues.
+ * Strictly speaking, this is not valid JSON. But when the output is being
+ * fed to a browser's JavaScript, it makes for smaller output and the
+ * browser can handle the output just fine.
+ * - "useSpecialFloats": false or true
+ * - If true, outputs non-finite floating point values in the following way:
+ * NaN values as "NaN", positive infinity as "Infinity", and negative
+ * infinity as "-Infinity".
+ * - "precision": int
+ * - Number of precision digits for formatting of real values.
+ * - "precisionType": "significant"(default) or "decimal"
+ * - Type of precision for formatting of real values.
+ * - "emitUTF8": false or true
+ * - If true, outputs raw UTF8 strings instead of escaping them.
+
+ * You can examine 'settings_` yourself
+ * to see the defaults. You can also write and read them just like any
+ * JSON Value.
+ * \sa setDefaults()
+ */
+ Json::Value settings_;
+
+ StreamWriterBuilder();
+ ~StreamWriterBuilder() override;
+
+ /**
+ * \throw std::exception if something goes wrong (e.g. invalid settings)
+ */
+ StreamWriter* newStreamWriter() const override;
+
+ /** \return true if 'settings' are legal and consistent;
+ * otherwise, indicate bad settings via 'invalid'.
+ */
+ bool validate(Json::Value* invalid) const;
+ /** A simple way to update a specific setting.
+ */
+ Value& operator[](const String& key);
+
+ /** Called by ctor, but you can use this to reset settings_.
+ * \pre 'settings' != NULL (but Json::null is fine)
+ * \remark Defaults:
+ * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults
+ */
+ static void setDefaults(Json::Value* settings);
+};
+
+/** \brief Abstract class for writers.
+ * \deprecated Use StreamWriter. (And really, this is an implementation detail.)
+ */
+class JSON_API Writer {
+public:
+ virtual ~Writer();
+
+ virtual String write(const Value& root) = 0;
+};
+
+/** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format
+ *without formatting (not human friendly).
+ *
+ * The JSON document is written in a single line. It is not intended for 'human'
+ *consumption,
+ * but may be useful to support feature such as RPC where bandwidth is limited.
+ * \sa Reader, Value
+ * \deprecated Use StreamWriterBuilder.
+ */
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4996) // Deriving from deprecated class
+#endif
+class JSON_API FastWriter
+ : public Writer {
+public:
+ FastWriter();
+ ~FastWriter() override = default;
+
+ void enableYAMLCompatibility();
+
+ /** \brief Drop the "null" string from the writer's output for nullValues.
+ * Strictly speaking, this is not valid JSON. But when the output is being
+ * fed to a browser's JavaScript, it makes for smaller output and the
+ * browser can handle the output just fine.
+ */
+ void dropNullPlaceholders();
+
+ void omitEndingLineFeed();
+
+public: // overridden from Writer
+ String write(const Value& root) override;
+
+private:
+ void writeValue(const Value& value);
+
+ String document_;
+ bool yamlCompatibilityEnabled_{false};
+ bool dropNullPlaceholders_{false};
+ bool omitEndingLineFeed_{false};
+};
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
+ *human friendly way.
+ *
+ * The rules for line break and indent are as follow:
+ * - Object value:
+ * - if empty then print {} without indent and line break
+ * - if not empty the print '{', line break & indent, print one value per
+ *line
+ * and then unindent and line break and print '}'.
+ * - Array value:
+ * - if empty then print [] without indent and line break
+ * - if the array contains no object value, empty array or some other value
+ *types,
+ * and all the values fit on one lines, then print the array on a single
+ *line.
+ * - otherwise, it the values do not fit on one line, or the array contains
+ * object or non empty array, then print one value per line.
+ *
+ * If the Value have comments then they are outputed according to their
+ *#CommentPlacement.
+ *
+ * \sa Reader, Value, Value::setComment()
+ * \deprecated Use StreamWriterBuilder.
+ */
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4996) // Deriving from deprecated class
+#endif
+class JSON_API
+ StyledWriter : public Writer {
+public:
+ StyledWriter();
+ ~StyledWriter() override = default;
+
+public: // overridden from Writer
+ /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
+ * \param root Value to serialize.
+ * \return String containing the JSON document that represents the root value.
+ */
+ String write(const Value& root) override;
+
+private:
+ void writeValue(const Value& value);
+ void writeArrayValue(const Value& value);
+ bool isMultilineArray(const Value& value);
+ void pushValue(const String& value);
+ void writeIndent();
+ void writeWithIndent(const String& value);
+ void indent();
+ void unindent();
+ void writeCommentBeforeValue(const Value& root);
+ void writeCommentAfterValueOnSameLine(const Value& root);
+ static bool hasCommentForValue(const Value& value);
+ static String normalizeEOL(const String& text);
+
+ using ChildValues = std::vector<String>;
+
+ ChildValues childValues_;
+ String document_;
+ String indentString_;
+ unsigned int rightMargin_{74};
+ unsigned int indentSize_{3};
+ bool addChildValues_{false};
+};
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
+ human friendly way,
+ to a stream rather than to a string.
+ *
+ * The rules for line break and indent are as follow:
+ * - Object value:
+ * - if empty then print {} without indent and line break
+ * - if not empty the print '{', line break & indent, print one value per
+ line
+ * and then unindent and line break and print '}'.
+ * - Array value:
+ * - if empty then print [] without indent and line break
+ * - if the array contains no object value, empty array or some other value
+ types,
+ * and all the values fit on one lines, then print the array on a single
+ line.
+ * - otherwise, it the values do not fit on one line, or the array contains
+ * object or non empty array, then print one value per line.
+ *
+ * If the Value have comments then they are outputed according to their
+ #CommentPlacement.
+ *
+ * \sa Reader, Value, Value::setComment()
+ * \deprecated Use StreamWriterBuilder.
+ */
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4996) // Deriving from deprecated class
+#endif
+class JSON_API
+ StyledStreamWriter {
+public:
+ /**
+ * \param indentation Each level will be indented by this amount extra.
+ */
+ StyledStreamWriter(String indentation = "\t");
+ ~StyledStreamWriter() = default;
+
+public:
+ /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
+ * \param out Stream to write to. (Can be ostringstream, e.g.)
+ * \param root Value to serialize.
+ * \note There is no point in deriving from Writer, since write() should not
+ * return a value.
+ */
+ void write(OStream& out, const Value& root);
+
+private:
+ void writeValue(const Value& value);
+ void writeArrayValue(const Value& value);
+ bool isMultilineArray(const Value& value);
+ void pushValue(const String& value);
+ void writeIndent();
+ void writeWithIndent(const String& value);
+ void indent();
+ void unindent();
+ void writeCommentBeforeValue(const Value& root);
+ void writeCommentAfterValueOnSameLine(const Value& root);
+ static bool hasCommentForValue(const Value& value);
+ static String normalizeEOL(const String& text);
+
+ using ChildValues = std::vector<String>;
+
+ ChildValues childValues_;
+ OStream* document_;
+ String indentString_;
+ unsigned int rightMargin_{74};
+ String indentation_;
+ bool addChildValues_ : 1;
+ bool indented_ : 1;
+};
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#if defined(JSON_HAS_INT64)
+String JSON_API valueToString(Int value);
+String JSON_API valueToString(UInt value);
+#endif // if defined(JSON_HAS_INT64)
+String JSON_API valueToString(LargestInt value);
+String JSON_API valueToString(LargestUInt value);
+String JSON_API valueToString(
+ double value, unsigned int precision = Value::defaultRealPrecision,
+ PrecisionType precisionType = PrecisionType::significantDigits);
+String JSON_API valueToString(bool value);
+String JSON_API valueToQuotedString(const char* value);
+
+/// \brief Output using the StyledStreamWriter.
+/// \see Json::operator>>()
+JSON_API OStream& operator<<(OStream&, const Value& root);
} // namespace Json
+#pragma pack(pop)
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(pop)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#endif // JSON_WRITER_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
// End of content of file: include/json/writer.h
// //////////////////////////////////////////////////////////////////////
-#endif //ifndef JSON_AMALGATED_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/assertions.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_ASSERTIONS_H_INCLUDED
+#define JSON_ASSERTIONS_H_INCLUDED
+
+#include <cstdlib>
+#include <sstream>
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "config.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+/** It should not be possible for a maliciously designed file to
+ * cause an abort() or seg-fault, so these macros are used only
+ * for pre-condition violations and internal logic errors.
+ */
+#if JSON_USE_EXCEPTION
+
+// @todo <= add detail about condition in exception
+#define JSON_ASSERT(condition) \
+ do { \
+ if (!(condition)) { \
+ Json::throwLogicError("assert json failed"); \
+ } \
+ } while (0)
+
+#define JSON_FAIL_MESSAGE(message) \
+ do { \
+ OStringStream oss; \
+ oss << message; \
+ Json::throwLogicError(oss.str()); \
+ abort(); \
+ } while (0)
+
+#else // JSON_USE_EXCEPTION
+
+#define JSON_ASSERT(condition) assert(condition)
+
+// The call to assert() will show the failure message in debug builds. In
+// release builds we abort, for a core-dump or debugger.
+#define JSON_FAIL_MESSAGE(message) \
+ { \
+ OStringStream oss; \
+ oss << message; \
+ assert(false && oss.str().c_str()); \
+ abort(); \
+ }
+
+#endif
+
+#define JSON_ASSERT_MESSAGE(condition, message) \
+ do { \
+ if (!(condition)) { \
+ JSON_FAIL_MESSAGE(message); \
+ } \
+ } while (0)
+
+#endif // JSON_ASSERTIONS_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/assertions.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+#endif //ifndef JSON_AMALGAMATED_H_INCLUDED
diff --git a/Modules/DICOM/CMakeLists.txt b/Modules/DICOM/CMakeLists.txt
index ebff7f5798..36966c5338 100644
--- a/Modules/DICOM/CMakeLists.txt
+++ b/Modules/DICOM/CMakeLists.txt
@@ -1,9 +1,9 @@
MITK_CREATE_MODULE(
DEPENDS MitkCore
PACKAGE_DEPENDS
- PUBLIC GDCM tinyxml2
+ PUBLIC GDCM|MSFF tinyxml2
PRIVATE DCMTK ITK|IOGDCM
)
add_subdirectory(test)
add_subdirectory(autoload/DICOMImageIO)
diff --git a/Modules/DICOM/autoload/DICOMImageIO/include/mitkDICOMTagsOfInterestService.h b/Modules/DICOM/autoload/DICOMImageIO/include/mitkDICOMTagsOfInterestService.h
index 85544efeea..d3eb0094d8 100644
--- a/Modules/DICOM/autoload/DICOMImageIO/include/mitkDICOMTagsOfInterestService.h
+++ b/Modules/DICOM/autoload/DICOMImageIO/include/mitkDICOMTagsOfInterestService.h
@@ -1,64 +1,62 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkDICOMTagsOfInterestService_h
#define mitkDICOMTagsOfInterestService_h
#include <string>
+#include <mutex>
#include <vector>
#include <set>
#include <mitkIDICOMTagsOfInterest.h>
-#include <itkFastMutexLock.h>
-#include <itkMutexLockHolder.h>
-
namespace mitk
{
/**
* \ingroup MicroServices_Interfaces
* \brief DICOM tags of interest service.
*
* This service allows you to manage the tags of interest (toi).
* All registred toi will be extracted when loading dicom data and stored as properties in the corresponding
* base data object. In addition the service can (if available) use IPropertyPersistance and IPropertyDescriptions
* to ensure that the tags of interests are also persisted and have a human readable descriptions.
*/
class DICOMTagsOfInterestService: public IDICOMTagsOfInterest
{
public:
DICOMTagsOfInterestService();
~DICOMTagsOfInterestService() override;
void AddTagOfInterest(const DICOMTagPath& tag, bool makePersistant = true) override;
DICOMTagPathMapType GetTagsOfInterest() const override;
bool HasTag(const DICOMTagPath& tag) const override;
void RemoveTag(const DICOMTagPath& tag) override;
void RemoveAllTags() override;
private:
typedef std::set<DICOMTagPath> InternalTagSetType;
- typedef itk::MutexLockHolder<itk::SimpleFastMutexLock> MutexHolder;
+ typedef std::lock_guard<std::mutex> MutexHolder;
InternalTagSetType m_Tags;
- mutable itk::SimpleFastMutexLock m_Lock;
+ mutable std::mutex m_Lock;
DICOMTagsOfInterestService(const DICOMTagsOfInterestService&);
DICOMTagsOfInterestService& operator=(const DICOMTagsOfInterestService&);
};
}
#endif
diff --git a/Modules/DICOM/include/mitkDICOMITKSeriesGDCMReader.h b/Modules/DICOM/include/mitkDICOMITKSeriesGDCMReader.h
index 743bb72645..cf01900334 100644
--- a/Modules/DICOM/include/mitkDICOMITKSeriesGDCMReader.h
+++ b/Modules/DICOM/include/mitkDICOMITKSeriesGDCMReader.h
@@ -1,377 +1,377 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkDICOMITKSeriesGDCMReader_h
#define mitkDICOMITKSeriesGDCMReader_h
+#include <mutex>
#include <stack>
-#include "itkMutexLock.h"
#include "mitkDICOMFileReader.h"
#include "mitkDICOMDatasetSorter.h"
#include "mitkDICOMGDCMImageFrameInfo.h"
#include "mitkEquiDistantBlocksSorter.h"
#include "mitkNormalDirectionConsistencySorter.h"
#include "MitkDICOMExports.h"
namespace itk
{
class TimeProbesCollectorBase;
}
namespace mitk
{
/**
\ingroup DICOMModule
\brief Flexible reader based on itk::ImageSeriesReader and GDCM, for single-slice modalities like CT, MR, PET, CR, etc.
Implements the loading processed as structured by DICOMFileReader offers configuration
of its loading strategy.
Documentation sections:
- \ref DICOMITKSeriesGDCMReader_LoadingStrategy
- \ref DICOMITKSeriesGDCMReader_ForcedConfiguration
- \ref DICOMITKSeriesGDCMReader_UserConfiguration
- \ref DICOMITKSeriesGDCMReader_GantryTilt
- \ref DICOMITKSeriesGDCMReader_Testing
- \ref DICOMITKSeriesGDCMReader_Internals
- \ref DICOMITKSeriesGDCMReader_RelatedClasses
- \ref DICOMITKSeriesGDCMReader_TiltInternals
- \ref DICOMITKSeriesGDCMReader_Condensing
\section DICOMITKSeriesGDCMReader_LoadingStrategy Loading strategy
The set of input files is processed by a number of DICOMDatasetSorter objects which may do two sort of things:
1. split a list of input frames into multiple lists, based on DICOM tags such as "Rows", "Columns", which cannot be mixed within a single mitk::Image
2. sort the frames within the input lists, based on the values of DICOM tags such as "Image Position Patient"
When the DICOMITKSeriesGDCMReader is configured with DICOMDatasetSorter%s, the list of input files is processed
as follows:
1. build an initial set of output groups, simply by grouping all input files.
2. for each configured DICOMDatasetSorter, process:
- for each output group:
1. set this group's files as input to the sorter
2. let the sorter sort (and split)
3. integrate the sorter's output groups with our own output groups
\section DICOMITKSeriesGDCMReader_ForcedConfiguration Forced Configuration
In all cases, the reader will add two DICOMDatasetSorter objects that are required to load
mitk::Images properly via itk::ImageSeriesReader:
1. As a \b first step, the input files will be split into groups that are not compatible because they differ in essential aspects:
- (0028,0010) Number of Rows
- (0028,0011) Number of Columns
- (0028,0030) Pixel Spacing
- (0018,1164) Imager Pixel Spacing
- (0020,0037) %Image Orientation (Patient)
- (0018,0050) Slice Thickness
- (0028,0008) Number of Frames
2. As are two forced \b last steps:
1. There will always be an instance of EquiDistantBlocksSorter,
which ensures that there is an equal distance between all the frames of an Image.
This is required to achieve correct geometrical positions in the mitk::Image,
i.e. it is essential to be able to make measurements in images.
- whether or not the distance is required to be orthogonal to the image planes is configured by SetFixTiltByShearing().
- during this check, we need to tolerate some minor errors in documented vs. calculated image origins.
The amount of tolerance can be adjusted by SetToleratedOriginOffset() and SetToleratedOriginOffsetToAdaptive().
Please see EquiDistantBlocksSorter for more details. The default should be good for most cases.
2. There is always an instance of NormalDirectionConsistencySorter,
which makes the order of images go along the image normals (see NormalDirectionConsistencySorter)
\section DICOMITKSeriesGDCMReader_UserConfiguration User Configuration
The user of this class can add more sorting steps (similar to the one described in above section) by calling AddSortingElement().
Usually, an application will add sorting by "Image Position Patient", by "Instance Number", and by other relevant tags here.
\section DICOMITKSeriesGDCMReader_GantryTilt Gantry tilt handling
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. <i>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.</i>).
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 "work" in conjunction with mitk::Image, DICOMITKSeriesGDCMReader is able to perform a correction for such series.
Whether or not such correction should be attempted is controlled by SetFixTiltByShearing(), the default being correction.
For details, see "Internals" below.
\section DICOMITKSeriesGDCMReader_Testing Testing
A number of tests is implemented in module DICOMTesting, which is documented at \ref DICOMTesting.
\section DICOMITKSeriesGDCMReader_Internals Class internals
Internally, the class is based on GDCM and it depends heavily on the gdcm::Scanner class.
Since the sorting elements (see DICOMDatasetSorter and DICOMSortCriterion) can access tags only via the DICOMDatasetAccess interface,
BUT DICOMITKSeriesGDCMReader holds a list of more specific classes DICOMGDCMImageFrameInfo, we must convert between the two
types sometimes. This explains the methods ToDICOMDatasetList(), FromDICOMDatasetList().
The intermediate result of all the sorting efforts is held in m_SortingResultInProgress,
which is modified through InternalExecuteSortingStep().
\subsection DICOMITKSeriesGDCMReader_RelatedClasses Overview of related classes
The following diagram gives an overview of the related classes:
\image html implementeditkseriesgdcmreader.jpg
\subsection DICOMITKSeriesGDCMReader_TiltInternals Details about the tilt correction
The gantry tilt "correction" algorithm fixes 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<TOutputImage>::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 html Modules/DICOM/doc/Doxygen/tilt-correction.jpg
\subsection DICOMITKSeriesGDCMReader_Condensing Sub-classes can condense multiple blocks into a single larger block
The sorting/splitting process described above is helpful for at least two more DICOM readers, which either try to load 3D+t images or which load diffusion data.
In both cases, a single pixel of the mitk::Image is made up of multiple values, in one case values over time, in the other case multiple measurements of a single point.
The specialized readers for these cases (e.g. ThreeDnTDICOMSeriesReader) can reuse most of the methods in DICOMITKSeriesGDCMReader,
except that they need an extra step after the usual sorting, in which they can merge already grouped 3D blocks. What blocks are merged
depends on the specialized reader's understanding of these images. To allow for such merging, a method Condense3DBlocks() is called
as an absolute last step of AnalyzeInputFiles(). Given this, a sub-class could implement only LoadImages() and Condense3DBlocks() instead
repeating most of AnalyzeInputFiles().
*/
class MITKDICOM_EXPORT DICOMITKSeriesGDCMReader : public DICOMFileReader
{
public:
mitkClassMacro( DICOMITKSeriesGDCMReader, DICOMFileReader );
mitkCloneMacro( DICOMITKSeriesGDCMReader );
itkFactorylessNewMacro( DICOMITKSeriesGDCMReader );
mitkNewMacro1Param( DICOMITKSeriesGDCMReader, unsigned int );
mitkNewMacro2Param( DICOMITKSeriesGDCMReader, unsigned int, bool );
/**
\brief Runs the sorting / splitting process described in \ref DICOMITKSeriesGDCMReader_LoadingStrategy.
Method required by DICOMFileReader.
*/
void AnalyzeInputFiles() override;
// void AllocateOutputImages();
/**
\brief Loads images using itk::ImageSeriesReader, potentially applies shearing to correct gantry tilt.
*/
bool LoadImages() override;
// re-implemented from super-class
bool CanHandleFile(const std::string& filename) override;
/**
\brief Add an element to the sorting procedure described in \ref DICOMITKSeriesGDCMReader_LoadingStrategy.
*/
virtual void AddSortingElement(DICOMDatasetSorter* sorter, bool atFront = false);
typedef const std::list<DICOMDatasetSorter::ConstPointer> ConstSorterList;
ConstSorterList GetFreelyConfiguredSortingElements() const;
/**
\brief Controls whether to "fix" tilted acquisitions by shearing the output (see \ref DICOMITKSeriesGDCMReader_GantryTilt).
*/
void SetFixTiltByShearing(bool on);
bool GetFixTiltByShearing() const;
/**
\brief Controls whether groups of only two images are accepted when ensuring consecutive slices via EquiDistantBlocksSorter.
*/
void SetAcceptTwoSlicesGroups(bool accept) const;
bool GetAcceptTwoSlicesGroups() const;
/**
\brief See \ref DICOMITKSeriesGDCMReader_ForcedConfiguration.
*/
void SetToleratedOriginOffsetToAdaptive(double fractionOfInterSliceDistanct = 0.3) const;
/**
\brief See \ref DICOMITKSeriesGDCMReader_ForcedConfiguration.
*/
void SetToleratedOriginOffset(double millimeters = 0.005) const;
/**
\brief Ignore all dicom tags that are non-essential for simple 3D volume import.
*/
void SetSimpleVolumeReading(bool read)
{
m_SimpleVolumeReading = read;
};
/**
\brief Ignore all dicom tags that are non-essential for simple 3D volume import.
*/
bool GetSimpleVolumeReading()
{
return m_SimpleVolumeReading;
};
double GetToleratedOriginError() const;
bool IsToleratedOriginOffsetAbsolute() const;
double GetDecimalPlacesForOrientation() const;
bool operator==(const DICOMFileReader& other) const override;
DICOMTagPathList GetTagsOfInterest() const override;
static int GetDefaultDecimalPlacesForOrientation()
{
return m_DefaultDecimalPlacesForOrientation;
}
static bool GetDefaultSimpleVolumeImport()
{
return m_DefaultSimpleVolumeImport;
}
static bool GetDefaultFixTiltByShearing()
{
return m_DefaultFixTiltByShearing;
}
protected:
void InternalPrintConfiguration(std::ostream& os) const override;
/// \brief Return active C locale
static std::string GetActiveLocale();
/**
\brief Remember current locale on stack, activate "C" locale.
"C" locale is required for correct parsing of numbers by itk::ImageSeriesReader
*/
void PushLocale() const;
/**
\brief Activate last remembered locale from locale stack
"C" locale is required for correct parsing of numbers by itk::ImageSeriesReader
*/
void PopLocale() const;
const static int m_DefaultDecimalPlacesForOrientation = 5;
const static bool m_DefaultSimpleVolumeImport = false;
const static bool m_DefaultFixTiltByShearing = true;
DICOMITKSeriesGDCMReader(unsigned int decimalPlacesForOrientation = m_DefaultDecimalPlacesForOrientation, bool simpleVolumeImport = m_DefaultSimpleVolumeImport);
~DICOMITKSeriesGDCMReader() override;
DICOMITKSeriesGDCMReader(const DICOMITKSeriesGDCMReader& other);
DICOMITKSeriesGDCMReader& operator=(const DICOMITKSeriesGDCMReader& other);
typedef std::vector<DICOMDatasetAccessingImageFrameList> SortingBlockList;
/**
\brief "Hook" for sub-classes, see \ref DICOMITKSeriesGDCMReader_Condensing
\return REMAINING blocks
*/
virtual SortingBlockList Condense3DBlocks(SortingBlockList& resultOf3DGrouping);
virtual DICOMTagCache::Pointer GetTagCache() const;
void SetTagCache( const DICOMTagCache::Pointer& ) override;
/// \brief Sorting step as described in \ref DICOMITKSeriesGDCMReader_LoadingStrategy
static SortingBlockList InternalExecuteSortingStep(
unsigned int sortingStepIndex,
const DICOMDatasetSorter::Pointer& sorter,
const SortingBlockList& input);
/// \brief Loads the mitk::Image by means of an itk::ImageSeriesReader
virtual bool LoadMitkImageForOutput(unsigned int o);
virtual bool LoadMitkImageForImageBlockDescriptor(DICOMImageBlockDescriptor& block) const;
/// \brief Describe this reader's confidence for given SOP class UID
static ReaderImplementationLevel GetReaderImplementationLevel(const std::string sopClassUID);
private:
/// \brief Creates the required sorting steps described in \ref DICOMITKSeriesGDCMReader_ForcedConfiguration
void EnsureMandatorySortersArePresent(unsigned int decimalPlacesForOrientation, bool simpleVolumeImport = false);
protected:
// NOT nice, made available to ThreeDnTDICOMSeriesReader due to lack of time
bool m_FixTiltByShearing; // could be removed by ITKDICOMSeriesReader NOT flagging tilt unless requested to fix it!
bool m_SimpleVolumeReading;
private:
SortingBlockList m_SortingResultInProgress;
typedef std::list<DICOMDatasetSorter::Pointer> SorterList;
SorterList m_Sorter;
protected:
// NOT nice, made available to ThreeDnTDICOMSeriesReader and ClassicDICOMSeriesReader due to lack of time
mitk::EquiDistantBlocksSorter::Pointer m_EquiDistantBlocksSorter;
mitk::NormalDirectionConsistencySorter::Pointer m_NormalDirectionConsistencySorter;
private:
- static itk::MutexLock::Pointer s_LocaleMutex;
+ static std::mutex s_LocaleMutex;
mutable std::stack<std::string> m_ReplacedCLocales;
mutable std::stack<std::locale> m_ReplacedCinLocales;
double m_DecimalPlacesForOrientation;
DICOMTagCache::Pointer m_TagCache;
bool m_ExternalCache;
};
}
#endif
diff --git a/Modules/DICOM/include/mitkDICOMTagScanner.h b/Modules/DICOM/include/mitkDICOMTagScanner.h
index 374899f943..15abdbc3d0 100644
--- a/Modules/DICOM/include/mitkDICOMTagScanner.h
+++ b/Modules/DICOM/include/mitkDICOMTagScanner.h
@@ -1,118 +1,118 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkDICOMTagScanner_h
#define mitkDICOMTagScanner_h
#include <stack>
-#include "itkMutexLock.h"
+#include <mutex>
#include "mitkDICOMEnums.h"
#include "mitkDICOMTagPath.h"
#include "mitkDICOMTagCache.h"
#include "mitkDICOMDatasetAccessingImageFrameInfo.h"
namespace mitk
{
/**
\ingroup DICOMModule
\brief Abstracts the tag scanning process for a set of DICOM files.
Formerly integrated as a part of DICOMITKSeriesGDCMReader, the tag
scanning part has been factored out into DICOMTagScanner classes
in order to allow a single scan for multiple reader alternatives. This
helps much in the selection process of e.g. DICOMFileReaderSelector.
This is an abstract base class for concrete scanner implementations.
@remark When used in a process where multiple classes will access the scan
results, care should be taken that all the tags and files of interest
are communicated to DICOMTagScanner before requesting the results!
*/
class MITKDICOM_EXPORT DICOMTagScanner : public itk::Object
{
public:
mitkClassMacroItkParent(DICOMTagScanner, itk::Object);
/**
\brief Add this tag to the scanning process.
*/
virtual void AddTag(const DICOMTag& tag) = 0;
/**
\brief Add a list of tags to the scanning process.
*/
virtual void AddTags(const DICOMTagList& tags) = 0;
/**
\brief Add this tag path to the scanning process.
*/
virtual void AddTagPath(const DICOMTagPath& path) = 0;
/**
\brief Add a list of tag pathes to the scanning process.
*/
virtual void AddTagPaths(const DICOMTagPathList& paths) = 0;
/**
\brief Define the list of files to scan.
This does not ADD to an internal list, but it replaces the
whole list of files.
*/
virtual void SetInputFiles(const StringList& filenames) = 0;
/**
\brief Start the scanning process.
Calling Scan() will invalidate previous scans, forgetting
all about files and tags from files that have been scanned
previously.
*/
virtual void Scan() = 0;
/**
\brief Retrieve a result list for file-by-file tag access.
*/
virtual DICOMDatasetAccessingImageFrameList GetFrameInfoList() const = 0;
/**
\brief Retrieve Pointer to the complete cache of the scan.
*/
virtual DICOMTagCache::Pointer GetScanCache() const = 0;
protected:
/** \brief Return active C locale */
static std::string GetActiveLocale();
/**
\brief Remember current locale on stack, activate "C" locale.
"C" locale is required for correct parsing of numbers by itk::ImageSeriesReader
*/
void PushLocale() const;
/**
\brief Activate last remembered locale from locale stack
"C" locale is required for correct parsing of numbers by itk::ImageSeriesReader
*/
void PopLocale() const;
DICOMTagScanner();
~DICOMTagScanner() override;
private:
- static itk::MutexLock::Pointer s_LocaleMutex;
+ static std::mutex s_LocaleMutex;
mutable std::stack<std::string> m_ReplacedCLocales;
mutable std::stack<std::locale> m_ReplacedCinLocales;
DICOMTagScanner(const DICOMTagScanner&);
};
}
#endif
diff --git a/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMRGBPixel.cpp b/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMRGBPixel.cpp
index 11b1d88ecf..0499b2d5cb 100644
--- a/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMRGBPixel.cpp
+++ b/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMRGBPixel.cpp
@@ -1,58 +1,58 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#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:
+ case itk::IOComponentEnum::UCHAR:
return LoadDICOMByITK<itk::RGBPixel<unsigned char>>(
filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::CHAR:
+ case itk::IOComponentEnum::CHAR:
return LoadDICOMByITK<itk::RGBPixel<char>>(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::USHORT:
+ case itk::IOComponentEnum::USHORT:
return LoadDICOMByITK<itk::RGBPixel<unsigned short>>(
filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::SHORT:
+ case itk::IOComponentEnum::SHORT:
return LoadDICOMByITK<itk::RGBPixel<short>>(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::UINT:
+ case itk::IOComponentEnum::UINT:
return LoadDICOMByITK<itk::RGBPixel<unsigned int>>(
filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::INT:
+ case itk::IOComponentEnum::INT:
return LoadDICOMByITK<itk::RGBPixel<int>>(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::ULONG:
+ case itk::IOComponentEnum::ULONG:
return LoadDICOMByITK<itk::RGBPixel<long unsigned int>>(
filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::LONG:
+ case itk::IOComponentEnum::LONG:
return LoadDICOMByITK<itk::RGBPixel<long int>>(
filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::FLOAT:
+ case itk::IOComponentEnum::FLOAT:
return LoadDICOMByITK<itk::RGBPixel<float>>(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::DOUBLE:
+ case itk::IOComponentEnum::DOUBLE:
return LoadDICOMByITK<itk::RGBPixel<double>>(
filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
default:
MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType();
return nullptr;
}
}
} // end namespace mitk
diff --git a/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMRGBPixel4D.cpp b/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMRGBPixel4D.cpp
index 1c6d31bb1f..b994049e43 100644
--- a/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMRGBPixel4D.cpp
+++ b/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMRGBPixel4D.cpp
@@ -1,63 +1,63 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkDicomSeriesReader.txx"
namespace mitk
{
Image::Pointer DicomSeriesReader::MultiplexLoadDICOMByITK4DRGBPixel(std::list<StringContainer> &imageBlocks,
ImageBlockDescriptor imageBlockDescriptor,
bool correctTilt,
const GantryTiltInformation &tiltInfo,
DcmIoType::Pointer &io,
CallbackCommand *command,
Image::Pointer preLoadedImageBlock)
{
switch (io->GetComponentType())
{
- case DcmIoType::UCHAR:
+ case itk::IOComponentEnum::UCHAR:
return LoadDICOMByITK4D<itk::RGBPixel<unsigned char>>(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::CHAR:
+ case itk::IOComponentEnum::CHAR:
return LoadDICOMByITK4D<itk::RGBPixel<char>>(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::USHORT:
+ case itk::IOComponentEnum::USHORT:
return LoadDICOMByITK4D<itk::RGBPixel<unsigned short>>(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::SHORT:
+ case itk::IOComponentEnum::SHORT:
return LoadDICOMByITK4D<itk::RGBPixel<short>>(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::UINT:
+ case itk::IOComponentEnum::UINT:
return LoadDICOMByITK4D<itk::RGBPixel<unsigned int>>(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::INT:
+ case itk::IOComponentEnum::INT:
return LoadDICOMByITK4D<itk::RGBPixel<int>>(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::ULONG:
+ case itk::IOComponentEnum::ULONG:
return LoadDICOMByITK4D<itk::RGBPixel<long unsigned int>>(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::LONG:
+ case itk::IOComponentEnum::LONG:
return LoadDICOMByITK4D<itk::RGBPixel<long int>>(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::FLOAT:
+ case itk::IOComponentEnum::FLOAT:
return LoadDICOMByITK4D<itk::RGBPixel<float>>(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::DOUBLE:
+ case itk::IOComponentEnum::DOUBLE:
return LoadDICOMByITK4D<itk::RGBPixel<double>>(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
default:
MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType();
return nullptr;
}
}
} // end namespace mitk
diff --git a/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMScalar.cpp b/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMScalar.cpp
index 46c8dc61b4..dae0bb8510 100644
--- a/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMScalar.cpp
+++ b/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMScalar.cpp
@@ -1,54 +1,54 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#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:
+ case itk::IOComponentEnum::UCHAR:
return LoadDICOMByITK<unsigned char>(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::CHAR:
+ case itk::IOComponentEnum::CHAR:
return LoadDICOMByITK<char>(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::USHORT:
+ case itk::IOComponentEnum::USHORT:
return LoadDICOMByITK<unsigned short>(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::SHORT:
+ case itk::IOComponentEnum::SHORT:
return LoadDICOMByITK<short>(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::UINT:
+ case itk::IOComponentEnum::UINT:
return LoadDICOMByITK<unsigned int>(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::INT:
+ case itk::IOComponentEnum::INT:
return LoadDICOMByITK<int>(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::ULONG:
+ case itk::IOComponentEnum::ULONG:
return LoadDICOMByITK<long unsigned int>(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::LONG:
+ case itk::IOComponentEnum::LONG:
return LoadDICOMByITK<long int>(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::FLOAT:
+ case itk::IOComponentEnum::FLOAT:
return LoadDICOMByITK<float>(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::DOUBLE:
+ case itk::IOComponentEnum::DOUBLE:
return LoadDICOMByITK<double>(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
default:
MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType();
return nullptr;
}
}
} // end namespace mitk
#include <legacy/mitkDicomSeriesReader.txx>
diff --git a/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMScalar4D.cpp b/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMScalar4D.cpp
index e5239e4d6c..3def708d24 100644
--- a/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMScalar4D.cpp
+++ b/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMScalar4D.cpp
@@ -1,65 +1,65 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkDicomSeriesReader.txx"
namespace mitk
{
Image::Pointer DicomSeriesReader::MultiplexLoadDICOMByITK4DScalar(std::list<StringContainer> &imageBlocks,
ImageBlockDescriptor imageBlockDescriptor,
bool correctTilt,
const GantryTiltInformation &tiltInfo,
DcmIoType::Pointer &io,
CallbackCommand *command,
Image::Pointer preLoadedImageBlock)
{
switch (io->GetComponentType())
{
- case DcmIoType::UCHAR:
+ case itk::IOComponentEnum::UCHAR:
return LoadDICOMByITK4D<unsigned char>(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::CHAR:
+ case itk::IOComponentEnum::CHAR:
return LoadDICOMByITK4D<char>(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::USHORT:
+ case itk::IOComponentEnum::USHORT:
return LoadDICOMByITK4D<unsigned short>(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::SHORT:
+ case itk::IOComponentEnum::SHORT:
return LoadDICOMByITK4D<short>(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::UINT:
+ case itk::IOComponentEnum::UINT:
return LoadDICOMByITK4D<unsigned int>(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::INT:
+ case itk::IOComponentEnum::INT:
return LoadDICOMByITK4D<int>(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::ULONG:
+ case itk::IOComponentEnum::ULONG:
return LoadDICOMByITK4D<long unsigned int>(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::LONG:
+ case itk::IOComponentEnum::LONG:
return LoadDICOMByITK4D<long int>(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::FLOAT:
+ case itk::IOComponentEnum::FLOAT:
return LoadDICOMByITK4D<float>(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
- case DcmIoType::DOUBLE:
+ case itk::IOComponentEnum::DOUBLE:
return LoadDICOMByITK4D<double>(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
default:
MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType();
return nullptr;
}
}
} // end namespace mitk
#include <legacy/mitkDicomSeriesReader.txx>
diff --git a/Modules/DICOM/src/legacy/mitkDicomSeriesReader.cpp b/Modules/DICOM/src/legacy/mitkDicomSeriesReader.cpp
index ac3cf8bbe5..df1ca813e5 100644
--- a/Modules/DICOM/src/legacy/mitkDicomSeriesReader.cpp
+++ b/Modules/DICOM/src/legacy/mitkDicomSeriesReader.cpp
@@ -1,1860 +1,1860 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// uncomment for learning more about the internal sorting mechanisms
//#define MBILOG_ENABLE_DEBUG
#include <legacy/mitkDicomSeriesReader.h>
#include <mitkImage.h>
#include <mitkImageCast.h>
#include <mitkLocaleSwitch.h>
#include <itkGDCMSeriesFileNames.h>
#include <gdcmAttribute.h>
#include <gdcmDirectory.h>
#include <gdcmPixmapReader.h>
#include <gdcmRAWCodec.h>
#include <gdcmScanner.h>
#include <gdcmSorter.h>
#include <gdcmStringFilter.h>
#include <gdcmUIDs.h>
#include "mitkProperties.h"
namespace mitk
{
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 "<unknown value of enum ReaderImplementationLevel>";
};
}
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 "<unknown value of enum PixelSpacingInterpretation>";
};
}
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";
// Misc
dictionary["0008|0005"] = "dicom.SpecificCharacterSet";
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 nullptr;
}
return node;
}
else
{
return nullptr;
}
}
bool DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames,
DataNode &node,
bool sort,
bool check_4d,
bool correctTilt,
UpdateCallBackMethod callback,
itk::SmartPointer<Image> preLoadedImageBlock)
{
if (filenames.empty())
{
MITK_DEBUG << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic.";
node.SetData(nullptr);
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 || io->GetPixelType() == itk::ImageIOBase::RGB)
+ if (io->GetPixelType() == itk::IOPixelEnum::SCALAR || io->GetPixelType() == itk::IOPixelEnum::RGB)
{
LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
}
if (node.GetData())
{
return true;
}
}
}
catch ( const itk::MemoryAllocationError &e )
{
MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what();
}
catch ( const 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, itk::SmartPointer<Image> 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<unsigned char, 4>
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<ImageType> 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
}
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
auto &tagValueMappings = const_cast<gdcm::Scanner::MappingType &>(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 (auto 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)
// TODO TODO TODO when angle -27 and tiltangle 63, this will never trigger the if-clause... useless
// check
// in this case! old bug..?!
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 (auto 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<gdcm::Scanner::TagToValue &>(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(!)
StringContainer filesStillToAnalyze = groupIter->second.GetFilenames();
std::string groupUID = groupIter->first;
unsigned int subgroup(0);
MITK_DEBUG << "Analyze group " << groupUID << " of " << groupIter->second.GetFilenames().size() << " files";
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 ( const 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 < IDifiedValue.size(); i++)
{
while (i < IDifiedValue.size() &&
!(IDifiedValue[i] == '.' || (IDifiedValue[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 ( const 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<StringContainer> imageBlock;
imageBlock.push_back(filenames);
CopyMetaDataToImageProperties(imageBlock, tagValueMappings_, io, blockInfo, image);
}
void DicomSeriesReader::CopyMetaDataToImageProperties(std::list<StringContainer> 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;
auto &tagValueMappings = const_cast<gdcm::Scanner::MappingType &>(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 (auto i = imageBlock.begin(); i != imageBlock.end(); i++, timeStep++)
{
const StringContainer &files = (*i);
unsigned int slice(0);
for (auto 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();
auto dictIter = dict.Begin();
while (dictIter != dict.End())
{
// MITK_DEBUG << "Key " << dictIter->first;
std::string value;
if (itk::ExposeMetaData<std::string>(dict, dictIter->first, value))
{
// MITK_DEBUG << "Value " << value;
auto 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<ReaderImplementationLevel>::New(blockInfo.GetReaderImplementationLevel()));
image->SetProperty("dicomseriesreader.PixelSpacingInterpretationString",
StringProperty::New(PixelSpacingInterpretationToString(blockInfo.GetPixelSpacingType())));
image->SetProperty("dicomseriesreader.PixelSpacingInterpretation",
GenericProperty<PixelSpacingInterpretation>::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);
}
void DicomSeriesReader::LoadDicom(const StringContainer &filenames,
DataNode &node,
bool sort,
bool load4D,
bool correctTilt,
UpdateCallBackMethod callback,
Image::Pointer preLoadedImageBlock)
{
mitk::LocaleSwitch localeSwitch("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) : nullptr;
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
auto &tagValueMappings = const_cast<gdcm::Scanner::MappingType &>(scanner.GetMappings());
std::list<StringContainer> 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);
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);
std::string patientName = "NoName";
if (node.GetProperty("dicom.patient.PatientsName"))
patientName = node.GetProperty("dicom.patient.PatientsName")->GetValueAsString();
node.SetData(image);
node.SetName(patientName);
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 ( const std::exception &e )
{
// reset locale then throw up
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::StringContainer> DicomSeriesReader::SortIntoBlocksFor3DplusT(
const StringContainer &presortedFilenames,
const gdcm::Scanner::MappingType &tagValueMappings,
bool /*sort*/,
bool &canLoadAs4D)
{
std::list<StringContainer> 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)
const gdcm::Tag tagModality(0x0008, 0x0060);
// 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())
{
const std::string &modality = tagToValueMap.find(tagModality)->second;
if (modality.compare("RTIMAGE ") == 0 || modality.compare("RTIMAGE") == 0)
{
MITK_WARN << "Modality " << modality << " is not fully supported yet.";
numberOfBlocks = 1;
break;
}
else
{
// 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)
+ if (io->GetPixelType() == itk::IOPixelEnum::SCALAR)
{
return MultiplexLoadDICOMByITKScalar(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
}
- else if (io->GetPixelType() == itk::ImageIOBase::RGB)
+ else if (io->GetPixelType() == itk::IOPixelEnum::RGB)
{
return MultiplexLoadDICOMByITKRGBPixel(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
}
else
{
return nullptr;
}
}
Image::Pointer DicomSeriesReader::MultiplexLoadDICOMByITK4D(std::list<StringContainer> &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)
+ if (io->GetPixelType() == itk::IOPixelEnum::SCALAR)
{
return MultiplexLoadDICOMByITK4DScalar(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
}
- else if (io->GetPixelType() == itk::ImageIOBase::RGB)
+ else if (io->GetPixelType() == itk::IOPixelEnum::RGB)
{
return MultiplexLoadDICOMByITK4DRGBPixel(
imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock);
}
else
{
return nullptr;
}
}
} // end namespace mitk
diff --git a/Modules/DICOM/src/mitkDICOMITKSeriesGDCMReader.cpp b/Modules/DICOM/src/mitkDICOMITKSeriesGDCMReader.cpp
index 5bb2224cbd..19a910e832 100644
--- a/Modules/DICOM/src/mitkDICOMITKSeriesGDCMReader.cpp
+++ b/Modules/DICOM/src/mitkDICOMITKSeriesGDCMReader.cpp
@@ -1,622 +1,622 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
//#define MBILOG_ENABLE_DEBUG
#define ENABLE_TIMING
#include <itkTimeProbesCollectorBase.h>
#include <gdcmUIDs.h>
#include "mitkDICOMITKSeriesGDCMReader.h"
#include "mitkITKDICOMSeriesReaderHelper.h"
#include "mitkGantryTiltInformation.h"
#include "mitkDICOMTagBasedSorter.h"
#include "mitkDICOMGDCMTagScanner.h"
-itk::MutexLock::Pointer mitk::DICOMITKSeriesGDCMReader::s_LocaleMutex = itk::MutexLock::New();
+std::mutex mitk::DICOMITKSeriesGDCMReader::s_LocaleMutex;
mitk::DICOMITKSeriesGDCMReader::DICOMITKSeriesGDCMReader( unsigned int decimalPlacesForOrientation, bool simpleVolumeImport )
: DICOMFileReader()
, m_FixTiltByShearing(m_DefaultFixTiltByShearing)
, m_SimpleVolumeReading( simpleVolumeImport )
, m_DecimalPlacesForOrientation( decimalPlacesForOrientation )
, m_ExternalCache(false)
{
this->EnsureMandatorySortersArePresent( decimalPlacesForOrientation, simpleVolumeImport );
}
mitk::DICOMITKSeriesGDCMReader::DICOMITKSeriesGDCMReader( const DICOMITKSeriesGDCMReader& other )
: DICOMFileReader( other )
, m_FixTiltByShearing( other.m_FixTiltByShearing)
, m_SortingResultInProgress( other.m_SortingResultInProgress )
, m_Sorter( other.m_Sorter )
, m_EquiDistantBlocksSorter( other.m_EquiDistantBlocksSorter->Clone() )
, m_NormalDirectionConsistencySorter( other.m_NormalDirectionConsistencySorter->Clone() )
, m_ReplacedCLocales( other.m_ReplacedCLocales )
, m_ReplacedCinLocales( other.m_ReplacedCinLocales )
, m_DecimalPlacesForOrientation( other.m_DecimalPlacesForOrientation )
, m_TagCache( other.m_TagCache )
, m_ExternalCache(other.m_ExternalCache)
{
}
mitk::DICOMITKSeriesGDCMReader::~DICOMITKSeriesGDCMReader()
{
}
mitk::DICOMITKSeriesGDCMReader& mitk::DICOMITKSeriesGDCMReader::
operator=( const DICOMITKSeriesGDCMReader& other )
{
if ( this != &other )
{
DICOMFileReader::operator =( other );
this->m_FixTiltByShearing = other.m_FixTiltByShearing;
this->m_SortingResultInProgress = other.m_SortingResultInProgress;
this->m_Sorter = other.m_Sorter; // TODO should clone the list items
this->m_EquiDistantBlocksSorter = other.m_EquiDistantBlocksSorter->Clone();
this->m_NormalDirectionConsistencySorter = other.m_NormalDirectionConsistencySorter->Clone();
this->m_ReplacedCLocales = other.m_ReplacedCLocales;
this->m_ReplacedCinLocales = other.m_ReplacedCinLocales;
this->m_DecimalPlacesForOrientation = other.m_DecimalPlacesForOrientation;
this->m_TagCache = other.m_TagCache;
}
return *this;
}
bool mitk::DICOMITKSeriesGDCMReader::operator==( const DICOMFileReader& other ) const
{
if ( const auto* otherSelf = dynamic_cast<const Self*>( &other ) )
{
if ( this->m_FixTiltByShearing == otherSelf->m_FixTiltByShearing
&& *( this->m_EquiDistantBlocksSorter ) == *( otherSelf->m_EquiDistantBlocksSorter )
&& ( fabs( this->m_DecimalPlacesForOrientation - otherSelf->m_DecimalPlacesForOrientation ) < eps ) )
{
// test sorters for equality
if ( this->m_Sorter.size() != otherSelf->m_Sorter.size() )
return false;
auto mySorterIter = this->m_Sorter.cbegin();
auto oSorterIter = otherSelf->m_Sorter.cbegin();
for ( ; mySorterIter != this->m_Sorter.cend() && oSorterIter != otherSelf->m_Sorter.cend();
++mySorterIter, ++oSorterIter )
{
if ( !( **mySorterIter == **oSorterIter ) )
return false; // this sorter differs
}
// nothing differs ==> all is equal
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
void mitk::DICOMITKSeriesGDCMReader::SetFixTiltByShearing( bool on )
{
this->Modified();
m_FixTiltByShearing = on;
}
bool mitk::DICOMITKSeriesGDCMReader::GetFixTiltByShearing() const
{
return m_FixTiltByShearing;
}
void mitk::DICOMITKSeriesGDCMReader::SetAcceptTwoSlicesGroups( bool accept ) const
{
this->Modified();
m_EquiDistantBlocksSorter->SetAcceptTwoSlicesGroups( accept );
}
bool mitk::DICOMITKSeriesGDCMReader::GetAcceptTwoSlicesGroups() const
{
return m_EquiDistantBlocksSorter->GetAcceptTwoSlicesGroups();
}
void mitk::DICOMITKSeriesGDCMReader::InternalPrintConfiguration( std::ostream& os ) const
{
unsigned int sortIndex( 1 );
for ( auto sorterIter = m_Sorter.cbegin(); sorterIter != m_Sorter.cend(); ++sortIndex, ++sorterIter )
{
os << "Sorting step " << sortIndex << ":" << std::endl;
( *sorterIter )->PrintConfiguration( os, " " );
}
os << "Sorting step " << sortIndex << ":" << std::endl;
m_EquiDistantBlocksSorter->PrintConfiguration( os, " " );
}
std::string mitk::DICOMITKSeriesGDCMReader::GetActiveLocale()
{
return setlocale( LC_NUMERIC, nullptr );
}
void mitk::DICOMITKSeriesGDCMReader::PushLocale() const
{
- s_LocaleMutex->Lock();
+ s_LocaleMutex.lock();
std::string currentCLocale = setlocale( LC_NUMERIC, nullptr );
m_ReplacedCLocales.push( currentCLocale );
setlocale( LC_NUMERIC, "C" );
std::locale currentCinLocale( std::cin.getloc() );
m_ReplacedCinLocales.push( currentCinLocale );
std::locale l( "C" );
std::cin.imbue( l );
- s_LocaleMutex->Unlock();
+ s_LocaleMutex.unlock();
}
void mitk::DICOMITKSeriesGDCMReader::PopLocale() const
{
- s_LocaleMutex->Lock();
+ s_LocaleMutex.lock();
if ( !m_ReplacedCLocales.empty() )
{
setlocale( LC_NUMERIC, m_ReplacedCLocales.top().c_str() );
m_ReplacedCLocales.pop();
}
else
{
MITK_WARN << "Mismatched PopLocale on DICOMITKSeriesGDCMReader.";
}
if ( !m_ReplacedCinLocales.empty() )
{
std::cin.imbue( m_ReplacedCinLocales.top() );
m_ReplacedCinLocales.pop();
}
else
{
MITK_WARN << "Mismatched PopLocale on DICOMITKSeriesGDCMReader.";
}
- s_LocaleMutex->Unlock();
+ s_LocaleMutex.unlock();
}
mitk::DICOMITKSeriesGDCMReader::SortingBlockList
mitk::DICOMITKSeriesGDCMReader::Condense3DBlocks( SortingBlockList& input )
{
return input; // to be implemented differently by sub-classes
}
#if defined( MBILOG_ENABLE_DEBUG ) || defined( ENABLE_TIMING )
#define timeStart( part ) timer.Start( part );
#define timeStop( part ) timer.Stop( part );
#else
#define timeStart( part )
#define timeStop( part )
#endif
void mitk::DICOMITKSeriesGDCMReader::AnalyzeInputFiles()
{
itk::TimeProbesCollectorBase timer;
timeStart( "Reset" );
this->ClearOutputs();
timeStop( "Reset" );
// prepare initial sorting (== list of input files)
const StringList inputFilenames = this->GetInputFiles();
timeStart( "Check input for DCM" );
if ( inputFilenames.empty() || !this->CanHandleFile( inputFilenames.front() ) // first
|| !this->CanHandleFile( inputFilenames.back() ) // last
|| !this->CanHandleFile( inputFilenames[inputFilenames.size() / 2] ) // roughly central file
)
{
// TODO a read-as-many-as-possible fallback could be implemented here
MITK_DEBUG << "Reader unable to process files..";
return;
}
timeStop( "Check input for DCM" );
// scan files for sorting-relevant tags
if ( m_TagCache.IsNull() || ( m_TagCache->GetMTime()<this->GetMTime() && !m_ExternalCache ))
{
timeStart( "Tag scanning" );
DICOMGDCMTagScanner::Pointer filescanner = DICOMGDCMTagScanner::New();
filescanner->SetInputFiles( inputFilenames );
filescanner->AddTagPaths( this->GetTagsOfInterest() );
PushLocale();
filescanner->Scan();
PopLocale();
m_TagCache = filescanner->GetScanCache(); // keep alive and make accessible to sub-classes
timeStop("Tag scanning");
}
else
{
// ensure that the tag cache contains our required tags AND files and has scanned!
}
m_SortingResultInProgress.clear();
m_SortingResultInProgress.push_back(m_TagCache->GetFrameInfoList());
// sort and split blocks as configured
timeStart( "Sorting frames" );
unsigned int sorterIndex = 0;
for ( auto sorterIter = m_Sorter.cbegin(); sorterIter != m_Sorter.cend(); ++sorterIndex, ++sorterIter )
{
std::stringstream ss;
ss << "Sorting step " << sorterIndex;
timeStart( ss.str().c_str() );
m_SortingResultInProgress =
this->InternalExecuteSortingStep( sorterIndex, *sorterIter, m_SortingResultInProgress );
timeStop( ss.str().c_str() );
}
if ( !m_SimpleVolumeReading )
{
// a last extra-sorting step: ensure equidistant slices
timeStart( "EquiDistantBlocksSorter" );
m_SortingResultInProgress = this->InternalExecuteSortingStep(
sorterIndex++, m_EquiDistantBlocksSorter.GetPointer(), m_SortingResultInProgress );
timeStop( "EquiDistantBlocksSorter" );
}
timeStop( "Sorting frames" );
timeStart( "Condensing 3D blocks" );
m_SortingResultInProgress = this->Condense3DBlocks( m_SortingResultInProgress );
timeStop( "Condensing 3D blocks" );
// provide final result as output
timeStart( "Output" );
unsigned int o = this->GetNumberOfOutputs();
this->SetNumberOfOutputs(
o + m_SortingResultInProgress.size() ); // Condense3DBlocks may already have added outputs!
for ( auto blockIter = m_SortingResultInProgress.cbegin(); blockIter != m_SortingResultInProgress.cend();
++o, ++blockIter )
{
const DICOMDatasetAccessingImageFrameList& gdcmFrameInfoList = *blockIter;
assert( !gdcmFrameInfoList.empty() );
// reverse frames if necessary
// update tilt information from absolute last sorting
const DICOMDatasetList datasetList = ConvertToDICOMDatasetList( gdcmFrameInfoList );
m_NormalDirectionConsistencySorter->SetInput( datasetList );
m_NormalDirectionConsistencySorter->Sort();
const DICOMDatasetAccessingImageFrameList sortedGdcmInfoFrameList =
ConvertToDICOMDatasetAccessingImageFrameList( m_NormalDirectionConsistencySorter->GetOutput( 0 ) );
const GantryTiltInformation& tiltInfo = m_NormalDirectionConsistencySorter->GetTiltInformation();
// set frame list for current block
const DICOMImageFrameList frameList = ConvertToDICOMImageFrameList( sortedGdcmInfoFrameList );
assert( !frameList.empty() );
DICOMImageBlockDescriptor block;
block.SetTagCache( this->GetTagCache() ); // important: this must be before SetImageFrameList(), because
// SetImageFrameList will trigger reading of lots of interesting
// tags!
block.SetAdditionalTagsOfInterest( GetAdditionalTagsOfInterest() );
block.SetTagLookupTableToPropertyFunctor( GetTagLookupTableToPropertyFunctor() );
block.SetImageFrameList( frameList );
block.SetTiltInformation( tiltInfo );
block.SetReaderImplementationLevel( this->GetReaderImplementationLevel( block.GetSOPClassUID() ) );
this->SetOutput( o, block );
}
timeStop( "Output" );
#if defined( MBILOG_ENABLE_DEBUG ) || defined( ENABLE_TIMING )
std::cout << "---------------------------------------------------------------" << std::endl;
timer.Report( std::cout );
std::cout << "---------------------------------------------------------------" << std::endl;
#endif
}
mitk::DICOMITKSeriesGDCMReader::SortingBlockList mitk::DICOMITKSeriesGDCMReader::InternalExecuteSortingStep(
unsigned int sortingStepIndex, const DICOMDatasetSorter::Pointer& sorter, const SortingBlockList& input )
{
SortingBlockList nextStepSorting; // we should not modify our input list while processing it
std::stringstream ss;
ss << "Sorting step " << sortingStepIndex << " '";
#if defined( MBILOG_ENABLE_DEBUG )
sorter->PrintConfiguration( ss );
#endif
ss << "'";
nextStepSorting.clear();
MITK_DEBUG << "================================================================================";
MITK_DEBUG << "DICOMITKSeriesGDCMReader: " << ss.str() << ": " << input.size() << " groups input";
unsigned int groupIndex = 0;
for ( auto blockIter = input.cbegin(); blockIter != input.cend(); ++groupIndex, ++blockIter )
{
const DICOMDatasetAccessingImageFrameList& gdcmInfoFrameList = *blockIter;
const DICOMDatasetList datasetList = ConvertToDICOMDatasetList( gdcmInfoFrameList );
#if defined( MBILOG_ENABLE_DEBUG )
MITK_DEBUG << "--------------------------------------------------------------------------------";
MITK_DEBUG << "DICOMITKSeriesGDCMReader: " << ss.str() << ", dataset group " << groupIndex << " ("
<< datasetList.size() << " datasets): ";
for ( auto oi = datasetList.cbegin(); oi != datasetList.cend(); ++oi )
{
MITK_DEBUG << " INPUT : " << ( *oi )->GetFilenameIfAvailable();
}
#endif
sorter->SetInput( datasetList );
sorter->Sort();
unsigned int numberOfResultingBlocks = sorter->GetNumberOfOutputs();
for ( unsigned int b = 0; b < numberOfResultingBlocks; ++b )
{
const DICOMDatasetList blockResult = sorter->GetOutput( b );
for ( auto oi = blockResult.cbegin(); oi != blockResult.cend(); ++oi )
{
MITK_DEBUG << " OUTPUT(" << b << ") :" << ( *oi )->GetFilenameIfAvailable();
}
DICOMDatasetAccessingImageFrameList sortedGdcmInfoFrameList = ConvertToDICOMDatasetAccessingImageFrameList( blockResult );
nextStepSorting.push_back( sortedGdcmInfoFrameList );
}
}
return nextStepSorting;
}
mitk::ReaderImplementationLevel
mitk::DICOMITKSeriesGDCMReader::GetReaderImplementationLevel( const std::string sopClassUID )
{
if ( sopClassUID.empty() )
{
return SOPClassUnknown;
}
gdcm::UIDs uidKnowledge;
uidKnowledge.SetFromUID( sopClassUID.c_str() );
gdcm::UIDs::TSName gdcmType = static_cast<gdcm::UIDs::TSName>((gdcm::UIDs::TSType)uidKnowledge);
switch ( gdcmType )
{
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 SOPClassSupported;
case gdcm::UIDs::NuclearMedicineImageStorage:
return SOPClassPartlySupported;
case gdcm::UIDs::SecondaryCaptureImageStorage:
return SOPClassImplemented;
default:
return SOPClassUnsupported;
}
}
// void AllocateOutputImages();
bool mitk::DICOMITKSeriesGDCMReader::LoadImages()
{
bool success = true;
unsigned int numberOfOutputs = this->GetNumberOfOutputs();
for ( unsigned int o = 0; o < numberOfOutputs; ++o )
{
success &= this->LoadMitkImageForOutput( o );
}
return success;
}
bool mitk::DICOMITKSeriesGDCMReader::LoadMitkImageForImageBlockDescriptor(
DICOMImageBlockDescriptor& block ) const
{
PushLocale();
const DICOMImageFrameList& frames = block.GetImageFrameList();
const GantryTiltInformation tiltInfo = block.GetTiltInformation();
bool hasTilt = tiltInfo.IsRegularGantryTilt();
ITKDICOMSeriesReaderHelper::StringContainer filenames;
filenames.reserve( frames.size() );
for ( auto frameIter = frames.cbegin(); frameIter != frames.cend(); ++frameIter )
{
filenames.push_back( ( *frameIter )->Filename );
}
mitk::ITKDICOMSeriesReaderHelper helper;
bool success( true );
try
{
mitk::Image::Pointer mitkImage = helper.Load( filenames, m_FixTiltByShearing && hasTilt, tiltInfo );
block.SetMitkImage( mitkImage );
}
catch ( const std::exception& e )
{
success = false;
MITK_ERROR << "Exception during image loading: " << e.what();
}
PopLocale();
return success;
}
bool mitk::DICOMITKSeriesGDCMReader::LoadMitkImageForOutput( unsigned int o )
{
DICOMImageBlockDescriptor& block = this->InternalGetOutput( o );
return this->LoadMitkImageForImageBlockDescriptor( block );
}
bool mitk::DICOMITKSeriesGDCMReader::CanHandleFile( const std::string& filename )
{
return ITKDICOMSeriesReaderHelper::CanHandleFile( filename );
}
void mitk::DICOMITKSeriesGDCMReader::AddSortingElement( DICOMDatasetSorter* sorter, bool atFront )
{
assert( sorter );
if ( atFront )
{
m_Sorter.push_front( sorter );
}
else
{
m_Sorter.push_back( sorter );
}
this->Modified();
}
mitk::DICOMITKSeriesGDCMReader::ConstSorterList
mitk::DICOMITKSeriesGDCMReader::GetFreelyConfiguredSortingElements() const
{
std::list<DICOMDatasetSorter::ConstPointer> result;
unsigned int sortIndex( 0 );
for ( auto sorterIter = m_Sorter.begin(); sorterIter != m_Sorter.end(); ++sortIndex, ++sorterIter )
{
if ( sortIndex > 0 ) // ignore first element (see EnsureMandatorySortersArePresent)
{
result.push_back( ( *sorterIter ).GetPointer() );
}
}
return result;
}
void mitk::DICOMITKSeriesGDCMReader::EnsureMandatorySortersArePresent(
unsigned int decimalPlacesForOrientation, bool simpleVolumeImport )
{
DICOMTagBasedSorter::Pointer splitter = DICOMTagBasedSorter::New();
splitter->AddDistinguishingTag( DICOMTag(0x0028, 0x0010) ); // Number of Rows
splitter->AddDistinguishingTag( DICOMTag(0x0028, 0x0011) ); // Number of Columns
splitter->AddDistinguishingTag( DICOMTag(0x0028, 0x0030) ); // Pixel Spacing
splitter->AddDistinguishingTag( DICOMTag(0x0018, 0x1164) ); // Imager Pixel Spacing
splitter->AddDistinguishingTag( DICOMTag(0x0020, 0x0037), new mitk::DICOMTagBasedSorter::CutDecimalPlaces(decimalPlacesForOrientation) ); // Image Orientation (Patient)
splitter->AddDistinguishingTag( DICOMTag(0x0018, 0x0050) ); // Slice Thickness
if ( simpleVolumeImport )
{
MITK_DEBUG << "Simple volume reading: ignoring number of frames";
}
else
{
splitter->AddDistinguishingTag( DICOMTag(0x0028, 0x0008) ); // Number of Frames
}
this->AddSortingElement( splitter, true ); // true = at front
if ( m_EquiDistantBlocksSorter.IsNull() )
{
m_EquiDistantBlocksSorter = mitk::EquiDistantBlocksSorter::New();
}
m_EquiDistantBlocksSorter->SetAcceptTilt( m_FixTiltByShearing );
if ( m_NormalDirectionConsistencySorter.IsNull() )
{
m_NormalDirectionConsistencySorter = mitk::NormalDirectionConsistencySorter::New();
}
}
void mitk::DICOMITKSeriesGDCMReader::SetToleratedOriginOffsetToAdaptive( double fractionOfInterSliceDistance ) const
{
assert( m_EquiDistantBlocksSorter.IsNotNull() );
m_EquiDistantBlocksSorter->SetToleratedOriginOffsetToAdaptive( fractionOfInterSliceDistance );
this->Modified();
}
void mitk::DICOMITKSeriesGDCMReader::SetToleratedOriginOffset( double millimeters ) const
{
assert( m_EquiDistantBlocksSorter.IsNotNull() );
m_EquiDistantBlocksSorter->SetToleratedOriginOffset( millimeters );
this->Modified();
}
double mitk::DICOMITKSeriesGDCMReader::GetToleratedOriginError() const
{
assert( m_EquiDistantBlocksSorter.IsNotNull() );
return m_EquiDistantBlocksSorter->GetToleratedOriginOffset();
}
bool mitk::DICOMITKSeriesGDCMReader::IsToleratedOriginOffsetAbsolute() const
{
assert( m_EquiDistantBlocksSorter.IsNotNull() );
return m_EquiDistantBlocksSorter->IsToleratedOriginOffsetAbsolute();
}
double mitk::DICOMITKSeriesGDCMReader::GetDecimalPlacesForOrientation() const
{
return m_DecimalPlacesForOrientation;
}
mitk::DICOMTagCache::Pointer mitk::DICOMITKSeriesGDCMReader::GetTagCache() const
{
return m_TagCache;
}
void mitk::DICOMITKSeriesGDCMReader::SetTagCache( const DICOMTagCache::Pointer& tagCache )
{
m_TagCache = tagCache;
m_ExternalCache = tagCache.IsNotNull();
}
mitk::DICOMTagPathList mitk::DICOMITKSeriesGDCMReader::GetTagsOfInterest() const
{
DICOMTagPathList completeList;
// check all configured sorters
for ( auto sorterIter = m_Sorter.cbegin(); sorterIter != m_Sorter.cend(); ++sorterIter )
{
assert( sorterIter->IsNotNull() );
const DICOMTagList tags = ( *sorterIter )->GetTagsOfInterest();
completeList.insert( completeList.end(), tags.cbegin(), tags.cend() );
}
// check our own forced sorters
DICOMTagList tags = m_EquiDistantBlocksSorter->GetTagsOfInterest();
completeList.insert( completeList.end(), tags.cbegin(), tags.cend() );
tags = m_NormalDirectionConsistencySorter->GetTagsOfInterest();
completeList.insert( completeList.end(), tags.cbegin(), tags.cend() );
// add the tags for DICOMImageBlockDescriptor
tags = DICOMImageBlockDescriptor::GetTagsOfInterest();
completeList.insert( completeList.end(), tags.cbegin(), tags.cend() );
const AdditionalTagsMapType tagList = GetAdditionalTagsOfInterest();
for ( auto iter = tagList.cbegin();
iter != tagList.cend();
++iter
)
{
completeList.push_back( iter->first ) ;
}
return completeList;
}
diff --git a/Modules/DICOM/src/mitkDICOMTagScanner.cpp b/Modules/DICOM/src/mitkDICOMTagScanner.cpp
index 908ba5134f..7b2852eb85 100644
--- a/Modules/DICOM/src/mitkDICOMTagScanner.cpp
+++ b/Modules/DICOM/src/mitkDICOMTagScanner.cpp
@@ -1,72 +1,72 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkDICOMTagScanner.h"
-itk::MutexLock::Pointer mitk::DICOMTagScanner::s_LocaleMutex = itk::MutexLock::New();
+std::mutex mitk::DICOMTagScanner::s_LocaleMutex;
mitk::DICOMTagScanner::DICOMTagScanner()
{
}
mitk::DICOMTagScanner::~DICOMTagScanner()
{
}
void mitk::DICOMTagScanner::PushLocale() const
{
- s_LocaleMutex->Lock();
+ s_LocaleMutex.lock();
std::string currentCLocale = setlocale(LC_NUMERIC, nullptr);
m_ReplacedCLocales.push(currentCLocale);
setlocale(LC_NUMERIC, "C");
std::locale currentCinLocale(std::cin.getloc());
m_ReplacedCinLocales.push(currentCinLocale);
std::locale l("C");
std::cin.imbue(l);
- s_LocaleMutex->Unlock();
+ s_LocaleMutex.unlock();
}
void mitk::DICOMTagScanner::PopLocale() const
{
- s_LocaleMutex->Lock();
+ s_LocaleMutex.lock();
if (!m_ReplacedCLocales.empty())
{
setlocale(LC_NUMERIC, m_ReplacedCLocales.top().c_str());
m_ReplacedCLocales.pop();
}
else
{
MITK_WARN << "Mismatched PopLocale on DICOMITKSeriesGDCMReader.";
}
if (!m_ReplacedCinLocales.empty())
{
std::cin.imbue(m_ReplacedCinLocales.top());
m_ReplacedCinLocales.pop();
}
else
{
MITK_WARN << "Mismatched PopLocale on DICOMITKSeriesGDCMReader.";
}
- s_LocaleMutex->Unlock();
+ s_LocaleMutex.unlock();
}
std::string mitk::DICOMTagScanner::GetActiveLocale()
{
return setlocale(LC_NUMERIC, nullptr);
}
diff --git a/Modules/DICOM/src/mitkITKDICOMSeriesReaderHelper.cpp b/Modules/DICOM/src/mitkITKDICOMSeriesReaderHelper.cpp
index 47e1ca4b63..5ac21da5d4 100644
--- a/Modules/DICOM/src/mitkITKDICOMSeriesReaderHelper.cpp
+++ b/Modules/DICOM/src/mitkITKDICOMSeriesReaderHelper.cpp
@@ -1,455 +1,455 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
//#define MBILOG_ENABLE_DEBUG
#include <dcmtk/dcmdata/dcvrdt.h>
#include <dcmtk/ofstd/ofstd.h>
#define BOOST_DATE_TIME_NO_LIB
//Prevent unnecessary/unwanted auto link in this compilation when activating boost libraries in the MITK superbuild
//It is necessary because BOOST_ALL_DYN_LINK overwrites BOOST_DATE_TIME_NO_LIB
#if defined(BOOST_ALL_DYN_LINK)
#undef BOOST_ALL_DYN_LINK
#endif
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include "mitkITKDICOMSeriesReaderHelper.h"
#include "mitkITKDICOMSeriesReaderHelper.txx"
#include "mitkDICOMGDCMTagScanner.h"
#include "mitkArbitraryTimeGeometry.h"
#include "dcmtk/dcmdata/dcvrda.h"
const mitk::DICOMTag mitk::ITKDICOMSeriesReaderHelper::AcquisitionDateTag = mitk::DICOMTag( 0x0008, 0x0022 );
const mitk::DICOMTag mitk::ITKDICOMSeriesReaderHelper::AcquisitionTimeTag = mitk::DICOMTag( 0x0008, 0x0032 );
const mitk::DICOMTag mitk::ITKDICOMSeriesReaderHelper::TriggerTimeTag = mitk::DICOMTag( 0x0018, 0x1060 );
#define switch3DCase( IOType, T ) \
case IOType: \
return LoadDICOMByITK<T>( filenames, correctTilt, tiltInfo, io );
bool mitk::ITKDICOMSeriesReaderHelper::CanHandleFile( const std::string& filename )
{
MITK_DEBUG << "ITKDICOMSeriesReaderHelper::CanHandleFile " << filename;
itk::GDCMImageIO::Pointer tester = itk::GDCMImageIO::New();
return tester->CanReadFile( filename.c_str() );
}
mitk::Image::Pointer mitk::ITKDICOMSeriesReaderHelper::Load( const StringContainer& filenames,
bool correctTilt,
const GantryTiltInformation& tiltInfo )
{
if ( filenames.empty() )
{
MITK_DEBUG
<< "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic.";
return nullptr; // this is not actually an error but the result is very simple
}
typedef itk::GDCMImageIO DcmIoType;
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::IOPixelEnum::SCALAR )
{
switch ( io->GetComponentType() )
{
- switch3DCase(DcmIoType::UCHAR, unsigned char) switch3DCase(DcmIoType::CHAR, char) switch3DCase(
- DcmIoType::USHORT, unsigned short) switch3DCase(DcmIoType::SHORT, short)
- switch3DCase(DcmIoType::UINT, unsigned int) switch3DCase(DcmIoType::INT, int) switch3DCase(
- DcmIoType::ULONG, long unsigned int) switch3DCase(DcmIoType::LONG, long int)
- switch3DCase(DcmIoType::FLOAT, float) switch3DCase(DcmIoType::DOUBLE, double) default
+ switch3DCase(itk::IOComponentEnum::UCHAR, unsigned char) switch3DCase(itk::IOComponentEnum::CHAR, char) switch3DCase(
+ itk::IOComponentEnum::USHORT, unsigned short) switch3DCase(itk::IOComponentEnum::SHORT, short)
+ switch3DCase(itk::IOComponentEnum::UINT, unsigned int) switch3DCase(itk::IOComponentEnum::INT, int) switch3DCase(
+ itk::IOComponentEnum::ULONG, long unsigned int) switch3DCase(itk::IOComponentEnum::LONG, long int)
+ switch3DCase(itk::IOComponentEnum::FLOAT, float) switch3DCase(itk::IOComponentEnum::DOUBLE, double) default
: MITK_ERROR
<< "Found unsupported DICOM scalar pixel type: (enum value) "
<< io->GetComponentType();
}
}
- else if ( io->GetPixelType() == itk::ImageIOBase::RGB )
+ else if ( io->GetPixelType() == itk::IOPixelEnum::RGB )
{
switch ( io->GetComponentType() )
{
- switch3DCase(DcmIoType::UCHAR, itk::RGBPixel<unsigned char>) switch3DCase(
- DcmIoType::CHAR, itk::RGBPixel<char>) switch3DCase(DcmIoType::USHORT,
+ switch3DCase(itk::IOComponentEnum::UCHAR, itk::RGBPixel<unsigned char>) switch3DCase(
+ itk::IOComponentEnum::CHAR, itk::RGBPixel<char>) switch3DCase(itk::IOComponentEnum::USHORT,
itk::RGBPixel<unsigned short>)
- switch3DCase(DcmIoType::SHORT, itk::RGBPixel<short>) switch3DCase(
- DcmIoType::UINT, itk::RGBPixel<unsigned int>) switch3DCase(DcmIoType::INT, itk::RGBPixel<int>)
- switch3DCase(DcmIoType::ULONG, itk::RGBPixel<long unsigned int>)
- switch3DCase(DcmIoType::LONG, itk::RGBPixel<long int>) switch3DCase(
- DcmIoType::FLOAT, itk::RGBPixel<float>) switch3DCase(DcmIoType::DOUBLE,
+ switch3DCase(itk::IOComponentEnum::SHORT, itk::RGBPixel<short>) switch3DCase(
+ itk::IOComponentEnum::UINT, itk::RGBPixel<unsigned int>) switch3DCase(itk::IOComponentEnum::INT, itk::RGBPixel<int>)
+ switch3DCase(itk::IOComponentEnum::ULONG, itk::RGBPixel<long unsigned int>)
+ switch3DCase(itk::IOComponentEnum::LONG, itk::RGBPixel<long int>) switch3DCase(
+ itk::IOComponentEnum::FLOAT, itk::RGBPixel<float>) switch3DCase(itk::IOComponentEnum::DOUBLE,
itk::RGBPixel<double>) default
: MITK_ERROR
<< "Found unsupported DICOM scalar pixel type: (enum value) "
<< io->GetComponentType();
}
}
MITK_ERROR << "Unsupported DICOM pixel type";
return nullptr;
}
}
catch ( const itk::MemoryAllocationError& e )
{
MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what();
}
catch ( const std::exception& e )
{
MITK_ERROR << "Error encountered when loading DICOM series:" << e.what();
}
catch ( ... )
{
MITK_ERROR << "Unspecified error encountered when loading DICOM series.";
}
return nullptr;
}
#define switch3DnTCase( IOType, T ) \
case IOType: \
return LoadDICOMByITK3DnT<T>( filenamesLists, correctTilt, tiltInfo, io );
mitk::Image::Pointer mitk::ITKDICOMSeriesReaderHelper::Load3DnT( const StringContainerList& filenamesLists,
bool correctTilt,
const GantryTiltInformation& tiltInfo )
{
if ( filenamesLists.empty() || filenamesLists.front().empty() )
{
MITK_DEBUG
<< "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic.";
return nullptr; // this is not actually an error but the result is very simple
}
typedef itk::GDCMImageIO DcmIoType;
DcmIoType::Pointer io = DcmIoType::New();
try
{
if ( io->CanReadFile( filenamesLists.front().front().c_str() ) )
{
io->SetFileName( filenamesLists.front().front().c_str() );
io->ReadImageInformation();
- if ( io->GetPixelType() == itk::ImageIOBase::SCALAR )
+ if ( io->GetPixelType() == itk::IOPixelEnum::SCALAR )
{
switch ( io->GetComponentType() )
{
- switch3DnTCase(DcmIoType::UCHAR, unsigned char) switch3DnTCase(DcmIoType::CHAR, char)
- switch3DnTCase(DcmIoType::USHORT, unsigned short) switch3DnTCase(
- DcmIoType::SHORT, short) switch3DnTCase(DcmIoType::UINT,
- unsigned int) switch3DnTCase(DcmIoType::INT, int)
- switch3DnTCase(DcmIoType::ULONG, long unsigned int) switch3DnTCase(DcmIoType::LONG, long int)
- switch3DnTCase(DcmIoType::FLOAT, float) switch3DnTCase(DcmIoType::DOUBLE, double) default
+ switch3DnTCase(itk::IOComponentEnum::UCHAR, unsigned char) switch3DnTCase(itk::IOComponentEnum::CHAR, char)
+ switch3DnTCase(itk::IOComponentEnum::USHORT, unsigned short) switch3DnTCase(
+ itk::IOComponentEnum::SHORT, short) switch3DnTCase(itk::IOComponentEnum::UINT,
+ unsigned int) switch3DnTCase(itk::IOComponentEnum::INT, int)
+ switch3DnTCase(itk::IOComponentEnum::ULONG, long unsigned int) switch3DnTCase(itk::IOComponentEnum::LONG, long int)
+ switch3DnTCase(itk::IOComponentEnum::FLOAT, float) switch3DnTCase(itk::IOComponentEnum::DOUBLE, double) default
: MITK_ERROR
<< "Found unsupported DICOM scalar pixel type: (enum value) "
<< io->GetComponentType();
}
}
- else if ( io->GetPixelType() == itk::ImageIOBase::RGB )
+ else if ( io->GetPixelType() == itk::IOPixelEnum::RGB )
{
switch ( io->GetComponentType() )
{
- switch3DnTCase(DcmIoType::UCHAR, itk::RGBPixel<unsigned char>)
- switch3DnTCase(DcmIoType::CHAR, itk::RGBPixel<char>) switch3DnTCase(
- DcmIoType::USHORT, itk::RGBPixel<unsigned short>) switch3DnTCase(DcmIoType::SHORT,
+ switch3DnTCase(itk::IOComponentEnum::UCHAR, itk::RGBPixel<unsigned char>)
+ switch3DnTCase(itk::IOComponentEnum::CHAR, itk::RGBPixel<char>) switch3DnTCase(
+ itk::IOComponentEnum::USHORT, itk::RGBPixel<unsigned short>) switch3DnTCase(itk::IOComponentEnum::SHORT,
itk::RGBPixel<short>)
- switch3DnTCase(DcmIoType::UINT, itk::RGBPixel<unsigned int>) switch3DnTCase(
- DcmIoType::INT, itk::RGBPixel<int>) switch3DnTCase(DcmIoType::ULONG,
+ switch3DnTCase(itk::IOComponentEnum::UINT, itk::RGBPixel<unsigned int>) switch3DnTCase(
+ itk::IOComponentEnum::INT, itk::RGBPixel<int>) switch3DnTCase(itk::IOComponentEnum::ULONG,
itk::RGBPixel<long unsigned int>)
- switch3DnTCase(DcmIoType::LONG, itk::RGBPixel<long int>) switch3DnTCase(
- DcmIoType::FLOAT, itk::RGBPixel<float>) switch3DnTCase(DcmIoType::DOUBLE,
+ switch3DnTCase(itk::IOComponentEnum::LONG, itk::RGBPixel<long int>) switch3DnTCase(
+ itk::IOComponentEnum::FLOAT, itk::RGBPixel<float>) switch3DnTCase(itk::IOComponentEnum::DOUBLE,
itk::RGBPixel<double>) default
: MITK_ERROR
<< "Found unsupported DICOM scalar pixel type: (enum value) "
<< io->GetComponentType();
}
}
MITK_ERROR << "Unsupported DICOM pixel type";
return nullptr;
}
}
catch ( const itk::MemoryAllocationError& e )
{
MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what();
}
catch ( const std::exception& e )
{
MITK_ERROR << "Error encountered when loading DICOM series:" << e.what();
}
catch ( ... )
{
MITK_ERROR << "Unspecified error encountered when loading DICOM series.";
}
return nullptr;
}
bool ConvertDICOMDateTimeString( const std::string& dateString,
const std::string& timeString,
OFDateTime& time )
{
OFString content( timeString.c_str() );
if ( !dateString.empty() )
{
content = OFString( dateString.c_str() ).append( content );
}
else
{
// This is a workaround for DICOM data that has an AquisitionTime but no AquisitionDate.
// In this case, we use the current date. That's not really nice, but is absolutely OK
// as we're only interested in the time anyways...
OFString currentDate;
DcmDate::getCurrentDate( currentDate );
content = currentDate.append( content );
}
const OFCondition result = DcmDateTime::getOFDateTimeFromString( content, time );
return result.good();
}
boost::posix_time::ptime ConvertOFDateTimeToPTime( const OFDateTime& time )
{
const boost::gregorian::date boostDate(
time.getDate().getYear(), time.getDate().getMonth(), time.getDate().getDay() );
const boost::posix_time::time_duration boostTime =
boost::posix_time::hours( time.getTime().getHour() )
+ boost::posix_time::minutes( time.getTime().getMinute() )
+ boost::posix_time::seconds( static_cast<int>(time.getTime().getSecond()) )
+ boost::posix_time::milliseconds( time.getTime().getMilliSecond() );
boost::posix_time::ptime result( boostDate, boostTime );
return result;
}
OFDateTime GetLowerDateTime( const OFDateTime& time1, const OFDateTime& time2 )
{
OFDateTime result = time1;
if ( ( time2.getDate() < time1.getDate() )
|| ( ( time2.getDate() == time1.getDate() ) && ( time2.getTime() < time1.getTime() ) ) )
{
result = time2;
}
return result;
}
OFDateTime GetUpperDateTime( const OFDateTime& time1, const OFDateTime& time2 )
{
OFDateTime result = time1;
if ( ( time2.getDate() > time1.getDate() )
|| ( ( time2.getDate() == time1.getDate() ) && ( time2.getTime() > time1.getTime() ) ) )
{
result = time2;
}
return result;
}
double ComputeMiliSecDuration( const OFDateTime& start, const OFDateTime& stop )
{
const boost::posix_time::ptime startTime = ConvertOFDateTimeToPTime( start );
const boost::posix_time::ptime stopTime = ConvertOFDateTimeToPTime( stop );
::boost::posix_time::time_duration duration = stopTime - startTime;
return duration.total_milliseconds();
}
bool mitk::ITKDICOMSeriesReaderHelper::ExtractDateTimeBoundsAndTriggerOfTimeStep(
const StringContainer& filenamesOfTimeStep, DateTimeBounds& bounds, TimeBounds& triggerBounds)
{
DICOMGDCMTagScanner::Pointer filescanner = DICOMGDCMTagScanner::New();
filescanner->SetInputFiles(filenamesOfTimeStep);
filescanner->AddTag(AcquisitionDateTag);
filescanner->AddTag(AcquisitionTimeTag);
filescanner->AddTag(TriggerTimeTag);
filescanner->Scan();
const DICOMDatasetAccessingImageFrameList frameList = filescanner->GetFrameInfoList();
bool result = false;
bool firstAq = true;
bool firstTr = true;
triggerBounds = TimeBounds(0.0);
for (auto pos = frameList.cbegin(); pos != frameList.cend(); ++pos)
{
const std::string aqDateStr = (*pos)->GetTagValueAsString(AcquisitionDateTag).value;
const std::string aqTimeStr = (*pos)->GetTagValueAsString(AcquisitionTimeTag).value;
const std::string triggerTimeStr = (*pos)->GetTagValueAsString(TriggerTimeTag).value;
OFDateTime aqDateTime;
const bool convertAqResult = ConvertDICOMDateTimeString(aqDateStr, aqTimeStr, aqDateTime);
OFBool convertTriggerResult;
mitk::ScalarType triggerTime = OFStandard::atof(triggerTimeStr.c_str(), &convertTriggerResult);
if (convertAqResult)
{
if (firstAq)
{
bounds[0] = aqDateTime;
bounds[1] = aqDateTime;
firstAq = false;
}
else
{
bounds[0] = GetLowerDateTime(bounds[0], aqDateTime);
bounds[1] = GetUpperDateTime(bounds[1], aqDateTime);
}
result = true;
}
if (convertTriggerResult)
{
if (firstTr)
{
triggerBounds[0] = triggerTime;
triggerBounds[1] = triggerTime;
firstTr = false;
}
else
{
triggerBounds[0] = std::min(triggerBounds[0], triggerTime);
triggerBounds[1] = std::max(triggerBounds[1], triggerTime);
}
result = true;
}
}
return result;
};
bool mitk::ITKDICOMSeriesReaderHelper::ExtractTimeBoundsOfTimeStep(
const StringContainer& filenamesOfTimeStep, TimeBounds& bounds, const OFDateTime& baselineDateTime )
{
DateTimeBounds aqDTBounds;
TimeBounds triggerBounds;
bool result = ExtractDateTimeBoundsAndTriggerOfTimeStep(filenamesOfTimeStep, aqDTBounds, triggerBounds);
mitk::ScalarType lowerBound = ComputeMiliSecDuration( baselineDateTime, aqDTBounds[0] );
mitk::ScalarType upperBound = ComputeMiliSecDuration( baselineDateTime, aqDTBounds[1] );
if ( lowerBound < mitk::eps || upperBound < mitk::eps )
{
lowerBound = triggerBounds[0];
upperBound = triggerBounds[1];
}
bounds[0] = lowerBound;
bounds[1] = upperBound;
return result;
};
mitk::ITKDICOMSeriesReaderHelper::TimeBoundsList
mitk::ITKDICOMSeriesReaderHelper::ExtractTimeBoundsOfTimeSteps(
const StringContainerList& filenamesOfTimeSteps )
{
TimeBoundsList result;
OFDateTime baseLine;
// extract the timebounds
DateTimeBounds baselineDateTimeBounds;
TimeBounds triggerBounds;
auto pos = filenamesOfTimeSteps.cbegin();
ExtractDateTimeBoundsAndTriggerOfTimeStep(*pos, baselineDateTimeBounds, triggerBounds);
baseLine = baselineDateTimeBounds[0];
// timebounds for baseline is 0
TimeBounds bounds( 0.0 );
result.push_back( bounds );
// iterate over the remaining timesteps
for ( ++pos;
pos != filenamesOfTimeSteps.cend();
++pos )
{
TimeBounds bounds( 0.0 );
TimeBounds dateTimeBounds;
// extract the timebounds relative to the baseline
if ( ExtractTimeBoundsOfTimeStep( *pos, dateTimeBounds, baseLine ) )
{
bounds[0] = dateTimeBounds[0];
bounds[1] = dateTimeBounds[1];
}
result.push_back( bounds );
}
return result;
};
mitk::TimeGeometry::Pointer
mitk::ITKDICOMSeriesReaderHelper::GenerateTimeGeometry( const BaseGeometry* templateGeometry,
const TimeBoundsList& boundsList )
{
TimeGeometry::Pointer timeGeometry;
double check = 0.0;
const auto boundListSize = boundsList.size();
for ( std::size_t pos = 0; pos < boundListSize; ++pos )
{
check += boundsList[pos][0];
check += boundsList[pos][1];
}
if ( check < mitk::eps )
{ // if all bounds are zero we assume that the bounds could not be correctly determined
// and as a fallback generate a time geometry in the old mitk style
ProportionalTimeGeometry::Pointer newTimeGeometry = ProportionalTimeGeometry::New();
newTimeGeometry->Initialize( templateGeometry, boundListSize );
timeGeometry = newTimeGeometry.GetPointer();
}
else
{
ArbitraryTimeGeometry::Pointer newTimeGeometry = ArbitraryTimeGeometry::New();
newTimeGeometry->ClearAllGeometries();
newTimeGeometry->ReserveSpaceForGeometries( boundListSize );
for ( std::size_t pos = 0; pos < boundListSize; ++pos )
{
TimeBounds bounds = boundsList[pos];
if ( pos + 1 < boundListSize )
{ //Currently we do not explicitly support "gaps" in the time coverage
//thus we set the max time bound of a time step to the min time bound
//of its successor.
bounds[1] = boundsList[pos + 1][0];
}
newTimeGeometry->AppendNewTimeStepClone(templateGeometry, bounds[0], bounds[1]);
}
timeGeometry = newTimeGeometry.GetPointer();
}
return timeGeometry;
};
diff --git a/Modules/DICOMTesting/include/mitkTestDICOMLoading.h b/Modules/DICOMTesting/include/mitkTestDICOMLoading.h
index 63289abc0b..ad041a45a6 100644
--- a/Modules/DICOMTesting/include/mitkTestDICOMLoading.h
+++ b/Modules/DICOMTesting/include/mitkTestDICOMLoading.h
@@ -1,111 +1,111 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkTestDICOMLoading_h
#define mitkTestDICOMLoading_h
#include "mitkClassicDICOMSeriesReader.h"
#include "mitkPropertyKeyPath.h"
#include "MitkDICOMTestingExports.h"
namespace mitk
{
class MITKDICOMTESTING_EXPORT TestDICOMLoading
{
public:
typedef std::list<Image::Pointer> ImageList;
TestDICOMLoading();
ImageList
LoadFiles( const StringList & files );
Image::Pointer
DecorateVerifyCachedImage( const StringList& files, mitk::Image::Pointer cachedImage );
Image::Pointer
DecorateVerifyCachedImage( const StringList& files, DICOMTagCache*, mitk::Image::Pointer cachedImage );
/**
\brief Dump relevant image information for later comparison.
\sa CompareImageInformationDumps
*/
std::string
DumpImageInformation( const Image* image );
/**
\brief Compare two image information dumps.
\return true, if dumps are sufficiently equal (see parameters)
\sa DumpImageInformation
*/
bool
CompareImageInformationDumps( const std::string& reference,
const std::string& test );
private:
typedef std::map<std::string,std::string> KeyValueMap;
ClassicDICOMSeriesReader::Pointer
BuildDICOMReader();
void SetDefaultLocale();
void ResetUserLocale();
- std::string ComponentTypeToString( int type );
+ std::string ComponentTypeToString( itk::IOComponentEnum type );
KeyValueMap ParseDump( const std::string& dump );
bool CompareSpacedValueFields( const std::string& reference,
const std::string& test,
double eps = mitk::eps );
/**
Compress whitespace in string
\param pString input string
\param pFill replacement whitespace (only whitespace in string after reduction)
\param pWhitespace characters handled as whitespace
*/
std::string reduce(const std::string& pString,
const std::string& pFill = " ",
const std::string& pWhitespace = " \t");
/**
Remove leading and trailing whitespace
\param pString input string
\param pWhitespace characters handled as whitespace
*/
std::string trim(const std::string& pString,
const std::string& pWhitespace = " \t");
template<typename T>
bool StringToNumber(const std::string& s, T& value)
{
std::stringstream stream(s);
stream >> value;
return (!stream.fail()) && (std::abs(value) <= std::numeric_limits<T>::max());
}
static void AddPropertyToDump(const mitk::PropertyKeyPath& key, const mitk::Image* image, std::stringstream& result);
const char* m_PreviousCLocale;
std::locale m_PreviousCppLocale;
};
}
#endif
diff --git a/Modules/DICOMTesting/src/mitkTestDICOMLoading.cpp b/Modules/DICOMTesting/src/mitkTestDICOMLoading.cpp
index 9d63c65aa9..d6a44162f7 100644
--- a/Modules/DICOMTesting/src/mitkTestDICOMLoading.cpp
+++ b/Modules/DICOMTesting/src/mitkTestDICOMLoading.cpp
@@ -1,585 +1,585 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
//#define MBILOG_ENABLE_DEBUG
#include "mitkTestDICOMLoading.h"
#include "mitkDICOMIOMetaInformationPropertyConstants.h"
#include "mitkDICOMProperty.h"
#include "mitkArbitraryTimeGeometry.h"
#include <stack>
#include <gdcmVersion.h>
#include <dcmtk/config/osconfig.h>
#include "itksys/SystemTools.hxx"
mitk::TestDICOMLoading::TestDICOMLoading()
:m_PreviousCLocale(nullptr)
{
}
void mitk::TestDICOMLoading::SetDefaultLocale()
{
// remember old locale only once
if (m_PreviousCLocale == nullptr)
{
m_PreviousCLocale = setlocale(LC_NUMERIC, nullptr);
// set to "C"
setlocale(LC_NUMERIC, "C");
m_PreviousCppLocale = std::cin.getloc();
std::locale l( "C" );
std::cin.imbue(l);
std::cout.imbue(l);
}
}
void mitk::TestDICOMLoading::ResetUserLocale()
{
if (m_PreviousCLocale)
{
setlocale(LC_NUMERIC, m_PreviousCLocale);
std::cin.imbue(m_PreviousCppLocale);
std::cout.imbue(m_PreviousCppLocale);
m_PreviousCLocale = nullptr;
}
}
mitk::TestDICOMLoading::ImageList
mitk::TestDICOMLoading
::LoadFiles( const StringList& files )
{
for (auto iter = files.begin();
iter != files.end();
++iter)
{
MITK_DEBUG << "File " << *iter;
}
ImageList result;
ClassicDICOMSeriesReader::Pointer reader = this->BuildDICOMReader();
reader->SetTagLookupTableToPropertyFunctor(mitk::GetDICOMPropertyForDICOMValuesFunctor);
reader->SetInputFiles( files );
reader->AnalyzeInputFiles();
reader->PrintOutputs(std::cout,true);
reader->LoadImages();
unsigned int numberOfImages = reader->GetNumberOfOutputs();
for (unsigned imageIndex = 0; imageIndex < numberOfImages; ++imageIndex)
{
const DICOMImageBlockDescriptor& block = reader->GetOutput(imageIndex);
result.push_back( block.GetMitkImage() );
}
return result;
}
mitk::ClassicDICOMSeriesReader::Pointer
mitk::TestDICOMLoading
::BuildDICOMReader()
{
ClassicDICOMSeriesReader::Pointer reader = ClassicDICOMSeriesReader::New();
reader->SetFixTiltByShearing(true);
return reader;
}
mitk::Image::Pointer
mitk::TestDICOMLoading
::DecorateVerifyCachedImage( const StringList& files, mitk::DICOMTagCache* tagCache, mitk::Image::Pointer cachedImage )
{
DICOMImageBlockDescriptor block;
DICOMImageFrameList framelist;
for (auto iter = files.begin();
iter != files.end();
++iter)
{
framelist.push_back( DICOMImageFrameInfo::New(*iter) );
}
block.SetImageFrameList( framelist );
block.SetTagCache( tagCache );
block.SetMitkImage( cachedImage ); // this should/will create a propertylist describing the image slices
return block.GetMitkImage();
}
mitk::Image::Pointer
mitk::TestDICOMLoading
::DecorateVerifyCachedImage( const StringList& files, mitk::Image::Pointer cachedImage )
{
ClassicDICOMSeriesReader::Pointer reader = this->BuildDICOMReader();
reader->SetTagLookupTableToPropertyFunctor(mitk::GetDICOMPropertyForDICOMValuesFunctor);
reader->SetInputFiles( files );
reader->AnalyzeInputFiles(); // This just creates a "tag cache and a nice DICOMImageBlockDescriptor.
// Both of these could also be produced in a different way. The only
// important thing is, that the DICOMImageBlockDescriptor knows a
// tag-cache object when PropertyDecorateCachedMitkImageForImageBlockDescriptor
// is called.
if ( reader->GetNumberOfOutputs() != 1 )
{
MITK_ERROR << "Reader produce " << reader->GetNumberOfOutputs() << " images instead of 1 expected..";
return nullptr;
}
DICOMImageBlockDescriptor block = reader->GetOutput(0); // creates a block copy
block.SetMitkImage( cachedImage ); // this should/will create a propertylist describing the image slices
return block.GetMitkImage();
}
std::string
-mitk::TestDICOMLoading::ComponentTypeToString(int type)
+mitk::TestDICOMLoading::ComponentTypeToString(itk::IOComponentEnum type)
{
- if (type == itk::ImageIOBase::UCHAR)
+ if (type == itk::IOComponentEnum::UCHAR)
return "UCHAR";
- else if (type == itk::ImageIOBase::CHAR)
+ else if (type == itk::IOComponentEnum::CHAR)
return "CHAR";
- else if (type == itk::ImageIOBase::USHORT)
+ else if (type == itk::IOComponentEnum::USHORT)
return "USHORT";
- else if (type == itk::ImageIOBase::SHORT)
+ else if (type == itk::IOComponentEnum::SHORT)
return "SHORT";
- else if (type == itk::ImageIOBase::UINT)
+ else if (type == itk::IOComponentEnum::UINT)
return "UINT";
- else if (type == itk::ImageIOBase::INT)
+ else if (type == itk::IOComponentEnum::INT)
return "INT";
- else if (type == itk::ImageIOBase::ULONG)
+ else if (type == itk::IOComponentEnum::ULONG)
return "ULONG";
- else if (type == itk::ImageIOBase::LONG)
+ else if (type == itk::IOComponentEnum::LONG)
return "LONG";
- else if (type == itk::ImageIOBase::FLOAT)
+ else if (type == itk::IOComponentEnum::FLOAT)
return "FLOAT";
- else if (type == itk::ImageIOBase::DOUBLE)
+ else if (type == itk::IOComponentEnum::DOUBLE)
return "DOUBLE";
else
return "UNKNOWN";
}
// add a line to stringstream result (see DumpImageInformation
#define DumpLine(field, data) DumpILine(0, field, data)
// add an indented(!) line to stringstream result (see DumpImageInformation
#define DumpILine(indent, field, data) \
{ \
std::string DumpLine_INDENT; DumpLine_INDENT.resize(indent, ' ' ); \
result << DumpLine_INDENT << field << ": " << data << "\n"; \
}
std::string
mitk::TestDICOMLoading::DumpImageInformation( const Image* image )
{
std::stringstream result;
if (image == nullptr) return result.str();
SetDefaultLocale();
// basic image data
DumpLine( "Pixeltype", ComponentTypeToString(image->GetPixelType().GetComponentType()) );
DumpLine( "BitsPerPixel", image->GetPixelType().GetBpe() );
DumpLine( "Dimension", image->GetDimension() );
result << "Dimensions: ";
for (unsigned int dim = 0; dim < image->GetDimension(); ++dim)
result << image->GetDimension(dim) << " ";
result << "\n";
// geometry data
result << "Geometry: \n";
const TimeGeometry* timeGeometry = image->GetTimeGeometry();
BaseGeometry* geometry = timeGeometry->GetGeometryForTimeStep(0);
if (geometry)
{
AffineTransform3D* transform = geometry->GetIndexToWorldTransform();
if (transform)
{
result << " " << "Matrix: ";
const AffineTransform3D::MatrixType& matrix = transform->GetMatrix();
for (unsigned int i = 0; i < 3; ++i)
for (unsigned int j = 0; j < 3; ++j)
result << matrix[i][j] << " ";
result << "\n";
result << " " << "Offset: ";
const AffineTransform3D::OutputVectorType& offset = transform->GetOffset();
for (unsigned int i = 0; i < 3; ++i)
result << offset[i] << " ";
result << "\n";
result << " " << "Center: ";
const AffineTransform3D::InputPointType& center = transform->GetCenter();
for (unsigned int i = 0; i < 3; ++i)
result << center[i] << " ";
result << "\n";
result << " " << "Translation: ";
const AffineTransform3D::OutputVectorType& translation = transform->GetTranslation();
for (unsigned int i = 0; i < 3; ++i)
result << translation[i] << " ";
result << "\n";
result << " " << "Scale: ";
const double* scale = transform->GetScale();
for (unsigned int i = 0; i < 3; ++i)
result << scale[i] << " ";
result << "\n";
result << " " << "Origin: ";
const Point3D& origin = geometry->GetOrigin();
for (unsigned int i = 0; i < 3; ++i)
result << origin[i] << " ";
result << "\n";
result << " " << "Spacing: ";
const Vector3D& spacing = geometry->GetSpacing();
for (unsigned int i = 0; i < 3; ++i)
result << spacing[i] << " ";
result << "\n";
result << " " << "TimeBounds: ";
///////////////////////////////////////
// Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details.
// This workarround should be removed as soon as T28262 is solved!
TimeBounds timeBounds = timeGeometry->GetTimeBounds();
auto atg = dynamic_cast<const mitk::ArbitraryTimeGeometry*>(timeGeometry);
if (atg && atg->HasCollapsedFinalTimeStep())
{
timeBounds[1] = timeBounds[1] - 1.;
}
//Original code:
//const TimeBounds timeBounds = timeGeometry->GetTimeBounds();
//
// End of workarround for T27883
//////////////////////////////////////
for (unsigned int i = 0; i < 2; ++i)
result << timeBounds[i] << " ";
result << "\n";
}
}
// io dicom meta information
AddPropertyToDump(mitk::DICOMIOMetaInformationPropertyConstants::READER_CONFIGURATION(), image, result);
AddPropertyToDump(mitk::DICOMIOMetaInformationPropertyConstants::READER_FILES(), image, result);
AddPropertyToDump(mitk::DICOMIOMetaInformationPropertyConstants::READER_GANTRY_TILT_CORRECTED(), image, result);
AddPropertyToDump(mitk::DICOMIOMetaInformationPropertyConstants::READER_IMPLEMENTATION_LEVEL(), image, result);
AddPropertyToDump(mitk::DICOMIOMetaInformationPropertyConstants::READER_IMPLEMENTATION_LEVEL_STRING(), image, result);
AddPropertyToDump(mitk::DICOMIOMetaInformationPropertyConstants::READER_PIXEL_SPACING_INTERPRETATION(), image, result);
AddPropertyToDump(mitk::DICOMIOMetaInformationPropertyConstants::READER_PIXEL_SPACING_INTERPRETATION_STRING(), image, result);
AddPropertyToDump(mitk::DICOMIOMetaInformationPropertyConstants::READER_3D_plus_t(), image, result);
AddPropertyToDump(mitk::DICOMIOMetaInformationPropertyConstants::READER_DCMTK(), image, result);
AddPropertyToDump(mitk::DICOMIOMetaInformationPropertyConstants::READER_GDCM(), image, result);
ResetUserLocale();
return result.str();
}
void mitk::TestDICOMLoading::AddPropertyToDump(const mitk::PropertyKeyPath& key, const mitk::Image* image, std::stringstream& result)
{
auto propKey = mitk::PropertyKeyPathToPropertyName(key);
auto prop = image->GetProperty(propKey.c_str());
if (prop.IsNotNull())
{
auto value = prop->GetValueAsString();
auto dicomProp = dynamic_cast< mitk::DICOMProperty*>(prop.GetPointer());
if (dicomProp != nullptr)
{
auto strippedProp = dicomProp->Clone();
if (key == mitk::DICOMIOMetaInformationPropertyConstants::READER_FILES())
{//strip dicom file information from path to ensure generalized dump files
auto timePoints = strippedProp->GetAvailableTimeSteps();
for (auto timePoint : timePoints)
{
auto slices = strippedProp->GetAvailableSlices(timePoint);
for (auto slice : slices)
{
auto value = strippedProp->GetValue(timePoint, slice);
value = itksys::SystemTools::GetFilenameName(value);
strippedProp->SetValue(timePoint, slice, value);
}
}
}
value = mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(strippedProp);
}
result << propKey << ": " << value << "\n";
}
}
std::string
mitk::TestDICOMLoading::trim(const std::string& pString,
const std::string& pWhitespace)
{
const size_t beginStr = pString.find_first_not_of(pWhitespace);
if (beginStr == std::string::npos)
{
// no content
return "";
}
const size_t endStr = pString.find_last_not_of(pWhitespace);
const size_t range = endStr - beginStr + 1;
return pString.substr(beginStr, range);
}
std::string
mitk::TestDICOMLoading::reduce(const std::string& pString,
const std::string& pFill,
const std::string& pWhitespace)
{
// trim first
std::string result(trim(pString, pWhitespace));
// replace sub ranges
size_t beginSpace = result.find_first_of(pWhitespace);
while (beginSpace != std::string::npos)
{
const size_t endSpace =
result.find_first_not_of(pWhitespace, beginSpace);
const size_t range = endSpace - beginSpace;
result.replace(beginSpace, range, pFill);
const size_t newStart = beginSpace + pFill.length();
beginSpace = result.find_first_of(pWhitespace, newStart);
}
return result;
}
bool
mitk::TestDICOMLoading::CompareSpacedValueFields( const std::string& reference,
const std::string& test,
double /*eps*/ )
{
bool result(true);
// tokenize string, compare each token, if possible by float comparison
std::stringstream referenceStream(reduce(reference));
std::stringstream testStream(reduce(test));
std::string refToken;
std::string testToken;
while ( std::getline( referenceStream, refToken, ' ' ) &&
std::getline ( testStream, testToken, ' ' ) )
{
float refNumber;
float testNumber;
if ( this->StringToNumber(refToken, refNumber) )
{
if ( this->StringToNumber(testToken, testNumber) )
{
// print-out compared tokens if DEBUG output allowed
MITK_DEBUG << "Reference Token '" << refToken << "'" << " value " << refNumber
<< ", test Token '" << testToken << "'" << " value " << testNumber;
bool old_result = result;
result &= ( std::abs(refNumber - testNumber) < 0.0001f /*mitk::eps*/ );
// log the token/number which causes the test to fail
if( old_result != result)
{
MITK_ERROR << std::setprecision(16) << "Reference Token '" << refToken << "'" << " value " << refNumber
<< ", test Token '" << testToken << "'" << " value " << testNumber;
MITK_ERROR << "[FALSE] - difference: " << std::setprecision(16) << std::abs(refNumber - testNumber) << " EPS: " << 0.0001f; //mitk::eps;
}
}
else
{
MITK_ERROR << refNumber << " cannot be compared to '" << testToken << "'";
}
}
else
{
MITK_DEBUG << "Token '" << refToken << "'" << " handled as string";
result &= refToken == testToken;
}
}
if ( std::getline( referenceStream, refToken, ' ' ) )
{
MITK_ERROR << "Reference string still had values when test string was already parsed: ref '" << reference << "', test '" << test << "'";
result = false;
}
else if ( std::getline( testStream, testToken, ' ' ) )
{
MITK_ERROR << "Test string still had values when reference string was already parsed: ref '" << reference << "', test '" << test << "'";
result = false;
}
return result;
}
bool
mitk::TestDICOMLoading::CompareImageInformationDumps( const std::string& referenceDump,
const std::string& testDump )
{
KeyValueMap reference = ParseDump(referenceDump);
KeyValueMap test = ParseDump(testDump);
bool testResult(true);
// verify all expected values
for (KeyValueMap::const_iterator refIter = reference.begin();
refIter != reference.end();
++refIter)
{
const std::string& refKey = refIter->first;
const std::string& refValue = refIter->second;
if ( test.find(refKey) != test.end() )
{
const std::string& testValue = test[refKey];
if (refKey == mitk::PropertyKeyPathToPropertyName(mitk::DICOMIOMetaInformationPropertyConstants::READER_DCMTK()))
{ //check dcmtk version always against the current version of the system
bool thisTestResult = testValue == std::string(" ") + PACKAGE_VERSION;
testResult &= thisTestResult;
MITK_DEBUG << refKey << ": '" << PACKAGE_VERSION << "' == '" << testValue << "' ? " << (thisTestResult ? "YES" : "NO");
}
else if (refKey == mitk::PropertyKeyPathToPropertyName(mitk::DICOMIOMetaInformationPropertyConstants::READER_GDCM()))
{//check gdcm version always against the current version of the system
bool thisTestResult = testValue == std::string(" ") + gdcm::Version::GetVersion();
testResult &= thisTestResult;
MITK_DEBUG << refKey << ": '" << gdcm::Version::GetVersion() << "' == '" << testValue << "' ? " << (thisTestResult ? "YES" : "NO");
}
else
{
bool thisTestResult = CompareSpacedValueFields(refValue, testValue);
testResult &= thisTestResult;
MITK_DEBUG << refKey << ": '" << refValue << "' == '" << testValue << "' ? " << (thisTestResult ? "YES" : "NO");
}
}
else
{
MITK_ERROR << "Reference dump contains a key'" << refKey << "' (value '" << refValue << "')." ;
MITK_ERROR << "This key is expected to be generated for tests (but was not). Most probably you need to update your test data.";
return false;
}
}
// now check test dump does not contain any additional keys
for (KeyValueMap::const_iterator testIter = test.begin();
testIter != test.end();
++testIter)
{
const std::string& key = testIter->first;
const std::string& value = testIter->second;
if (key == mitk::PropertyKeyPathToPropertyName(mitk::DICOMIOMetaInformationPropertyConstants::READER_DCMTK()))
{//check dcmtk version always against the current version of the system
bool thisTestResult = value == std::string(" ")+PACKAGE_VERSION;
testResult &= thisTestResult;
MITK_DEBUG << key << ": '" << PACKAGE_VERSION << "' == '" << value << "' ? " << (thisTestResult ? "YES" : "NO");
}
else if (key == mitk::PropertyKeyPathToPropertyName(mitk::DICOMIOMetaInformationPropertyConstants::READER_GDCM()))
{//check gdcm version always against the current version of the system
bool thisTestResult = value == std::string(" ") + gdcm::Version::GetVersion();
testResult &= thisTestResult;
MITK_DEBUG << key << ": '" << gdcm::Version::GetVersion() << "' == '" << value << "' ? " << (thisTestResult ? "YES" : "NO");
}
else if ( reference.find(key) == reference.end() )
{
MITK_ERROR << "Test dump contains an unexpected key'" << key << "' (value '" << value << "')." ;
MITK_ERROR << "This key is not expected. Most probably you need to update your test data.";
return false;
}
}
return testResult;
}
mitk::TestDICOMLoading::KeyValueMap
mitk::TestDICOMLoading::ParseDump( const std::string& dump )
{
KeyValueMap parsedResult;
std::string shredder(dump);
std::stack<std::string> surroundingKeys;
std::stack<std::string::size_type> expectedIndents;
expectedIndents.push(0);
while (true)
{
std::string::size_type newLinePos = shredder.find( '\n' );
if (newLinePos == std::string::npos || newLinePos == 0) break;
std::string line = shredder.substr( 0, newLinePos );
shredder = shredder.erase( 0, newLinePos+1 );
std::string::size_type keyPosition = line.find_first_not_of( ' ' );
std::string::size_type colonPosition = line.find( ':' );
std::string key = line.substr(keyPosition, colonPosition - keyPosition);
std::string::size_type firstSpacePosition = key.find_first_of(" ");
if (firstSpacePosition != std::string::npos)
{
key.erase(firstSpacePosition);
}
if ( keyPosition > expectedIndents.top() )
{
// more indent than before
expectedIndents.push(keyPosition);
}
else
{
if (!surroundingKeys.empty())
{
surroundingKeys.pop(); // last of same length
}
while (expectedIndents.top() != keyPosition)
{
expectedIndents.pop();
if (!surroundingKeys.empty())
{
surroundingKeys.pop();
}
}; // unwind until current indent is found
}
if (!surroundingKeys.empty())
{
key = surroundingKeys.top() + "." + key; // construct current key name
}
surroundingKeys.push(key); // this is the new embracing key
std::string value = line.substr(colonPosition+1);
MITK_DEBUG << " Key: '" << key << "' value '" << value << "'" ;
parsedResult[key] = value; // store parsing result
}
return parsedResult;
}
diff --git a/Modules/DataTypesExt/include/mitkAffineBaseDataInteractor3D.h b/Modules/DataTypesExt/include/mitkAffineBaseDataInteractor3D.h
index 5774512c17..a1b875db38 100644
--- a/Modules/DataTypesExt/include/mitkAffineBaseDataInteractor3D.h
+++ b/Modules/DataTypesExt/include/mitkAffineBaseDataInteractor3D.h
@@ -1,105 +1,105 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkAffineBaseDataInteractor3D_h
#define mitkAffineBaseDataInteractor3D_h
#include "MitkDataTypesExtExports.h"
#include <mitkDataInteractor.h>
#include <mitkGeometry3D.h>
namespace mitk
{
////create events for interactions
#pragma GCC visibility push(default)
- itkEventMacro(AffineInteractionEvent, itk::AnyEvent);
- itkEventMacro(ScaleEvent, AffineInteractionEvent);
- itkEventMacro(RotateEvent, AffineInteractionEvent);
- itkEventMacro(TranslateEvent, AffineInteractionEvent);
+ itkEventMacroDeclaration(AffineInteractionEvent, itk::AnyEvent);
+ itkEventMacroDeclaration(ScaleEvent, AffineInteractionEvent);
+ itkEventMacroDeclaration(RotateEvent, AffineInteractionEvent);
+ itkEventMacroDeclaration(TranslateEvent, AffineInteractionEvent);
#pragma GCC visibility pop
/**
* \brief Affine interaction with mitk::BaseGeometry.
*
* \ingroup Interaction
*/
// Inherit from DataInteractor, this provides functionality of a state machine and configurable inputs.
class MITKDATATYPESEXT_EXPORT AffineBaseDataInteractor3D : public DataInteractor
{
public:
mitkClassMacro(AffineBaseDataInteractor3D, DataInteractor);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
void SetDataNode(DataNode *node) override;
void TranslateGeometry(mitk::Vector3D translate, mitk::BaseGeometry *geometry);
void RotateGeometry(mitk::ScalarType angle, int rotationaxis, mitk::BaseGeometry *geometry);
void ScaleGeometry(mitk::Point3D newScale, mitk::BaseGeometry *geometry);
mitk::BaseGeometry *GetUpdatedTimeGeometry(mitk::InteractionEvent *interactionEvent);
protected:
AffineBaseDataInteractor3D();
~AffineBaseDataInteractor3D() override;
/**
* Here actions strings from the loaded state machine pattern are mapped to functions of
* the DataInteractor. These functions are called when an action from the state machine pattern is executed.
*/
void ConnectActionsAndFunctions() override;
/**
* This function is called when a DataNode has been set/changed.
*/
void DataNodeChanged() override;
/**
* Initializes the movement, stores starting position.
*/
virtual bool CheckOverObject(const InteractionEvent *);
virtual void SelectObject(StateMachineAction *, InteractionEvent *);
virtual void DeselectObject(StateMachineAction *, InteractionEvent *);
virtual void InitTranslate(StateMachineAction *, InteractionEvent *);
virtual void InitRotate(StateMachineAction *, InteractionEvent *);
virtual void TranslateObject(StateMachineAction *, InteractionEvent *);
virtual void RotateObject(StateMachineAction *, InteractionEvent *);
virtual void ScaleObject(StateMachineAction *, InteractionEvent *);
virtual void TranslateUpKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void TranslateDownKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void TranslateLeftKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void TranslateRightKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void TranslateUpModifierKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void TranslateDownModifierKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void RotateUpKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void RotateDownKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void RotateLeftKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void RotateRightKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void RotateUpModifierKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void RotateDownModifierKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void ScaleDownKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent);
virtual void ScaleUpKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent);
virtual void RestoreNodeProperties();
/**
* @brief InitMembers convinience method to avoid code duplication between InitRotate() and InitTranslate().
* @param interactionEvent
*/
bool InitMembers(InteractionEvent *interactionEvent);
private:
Point3D m_InitialPickedWorldPoint;
Point2D m_InitialPickedDisplayPoint;
Geometry3D::Pointer m_OriginalGeometry;
};
}
#endif
diff --git a/Modules/DataTypesExt/src/mitkAffineBaseDataInteractor3D.cpp b/Modules/DataTypesExt/src/mitkAffineBaseDataInteractor3D.cpp
index 43bb1944cc..b33a752ab7 100644
--- a/Modules/DataTypesExt/src/mitkAffineBaseDataInteractor3D.cpp
+++ b/Modules/DataTypesExt/src/mitkAffineBaseDataInteractor3D.cpp
@@ -1,504 +1,512 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkAffineBaseDataInteractor3D.h"
#include <mitkInteractionConst.h>
#include <mitkInteractionPositionEvent.h>
#include <mitkRotationOperation.h>
#include <mitkScaleOperation.h>
#include <mitkSurface.h>
#include <mitkInteractionKeyEvent.h>
#include <vtkCamera.h>
#include <vtkInteractorObserver.h>
#include <vtkInteractorStyle.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
#include <vtkRenderWindowInteractor.h>
+namespace mitk
+{
+ itkEventMacroDefinition(AffineInteractionEvent, itk::AnyEvent);
+ itkEventMacroDefinition(ScaleEvent, AffineInteractionEvent);
+ itkEventMacroDefinition(RotateEvent, AffineInteractionEvent);
+ itkEventMacroDefinition(TranslateEvent, AffineInteractionEvent);
+}
+
// Properties to allow the user to interact with the base data
const char *translationStepSizePropertyName = "AffineBaseDataInteractor3D.Translation Step Size";
const char *selectedColorPropertyName = "AffineBaseDataInteractor3D.Selected Color";
const char *deselectedColorPropertyName = "AffineBaseDataInteractor3D.Deselected Color";
const char *priorPropertyName = "AffineBaseDataInteractor3D.Prior Color";
const char *rotationStepSizePropertyName = "AffineBaseDataInteractor3D.Rotation Step Size";
const char *scaleStepSizePropertyName = "AffineBaseDataInteractor3D.Scale Step Size";
const char *anchorPointX = "AffineBaseDataInteractor3D.Anchor Point X";
const char *anchorPointY = "AffineBaseDataInteractor3D.Anchor Point Y";
const char *anchorPointZ = "AffineBaseDataInteractor3D.Anchor Point Z";
mitk::AffineBaseDataInteractor3D::AffineBaseDataInteractor3D()
{
m_OriginalGeometry = mitk::Geometry3D::New();
}
mitk::AffineBaseDataInteractor3D::~AffineBaseDataInteractor3D()
{
this->RestoreNodeProperties();
}
void mitk::AffineBaseDataInteractor3D::ConnectActionsAndFunctions()
{
// **Conditions** that can be used in the state machine, to ensure that certain conditions are met, before actually
// executing an action
CONNECT_CONDITION("isOverObject", CheckOverObject);
// **Function** in the statmachine patterns also referred to as **Actions**
CONNECT_FUNCTION("selectObject", SelectObject);
CONNECT_FUNCTION("deselectObject", DeselectObject);
CONNECT_FUNCTION("initTranslate", InitTranslate);
CONNECT_FUNCTION("initRotate", InitRotate);
CONNECT_FUNCTION("translateObject", TranslateObject);
CONNECT_FUNCTION("rotateObject", RotateObject);
CONNECT_FUNCTION("scaleObject", ScaleObject);
CONNECT_FUNCTION("translateUpKey", TranslateUpKey);
CONNECT_FUNCTION("translateDownKey", TranslateDownKey);
CONNECT_FUNCTION("translateLeftKey", TranslateLeftKey);
CONNECT_FUNCTION("translateRightKey", TranslateRightKey);
CONNECT_FUNCTION("translateUpModifierKey", TranslateUpModifierKey);
CONNECT_FUNCTION("translateDownModifierKey", TranslateDownModifierKey);
CONNECT_FUNCTION("scaleDownKey", ScaleDownKey);
CONNECT_FUNCTION("scaleUpKey", ScaleUpKey);
CONNECT_FUNCTION("rotateUpKey", RotateUpKey);
CONNECT_FUNCTION("rotateDownKey", RotateDownKey);
CONNECT_FUNCTION("rotateLeftKey", RotateLeftKey);
CONNECT_FUNCTION("rotateRightKey", RotateRightKey);
CONNECT_FUNCTION("rotateUpModifierKey", RotateUpModifierKey);
CONNECT_FUNCTION("rotateDownModifierKey", RotateDownModifierKey);
}
void mitk::AffineBaseDataInteractor3D::TranslateUpKey(StateMachineAction *, InteractionEvent *interactionEvent)
{
float stepSize = 1.0f;
this->GetDataNode()->GetFloatProperty(translationStepSizePropertyName, stepSize);
mitk::Vector3D movementVector;
movementVector.Fill(0.0);
movementVector.SetElement(2, stepSize);
this->TranslateGeometry(movementVector, this->GetUpdatedTimeGeometry(interactionEvent));
}
void mitk::AffineBaseDataInteractor3D::TranslateDownKey(mitk::StateMachineAction *,
mitk::InteractionEvent *interactionEvent)
{
float stepSize = 1.0f;
this->GetDataNode()->GetFloatProperty(translationStepSizePropertyName, stepSize);
mitk::Vector3D movementVector;
movementVector.Fill(0.0);
movementVector.SetElement(2, -stepSize);
this->TranslateGeometry(movementVector, this->GetUpdatedTimeGeometry(interactionEvent));
}
void mitk::AffineBaseDataInteractor3D::TranslateLeftKey(mitk::StateMachineAction *,
mitk::InteractionEvent *interactionEvent)
{
float stepSize = 1.0f;
this->GetDataNode()->GetFloatProperty(translationStepSizePropertyName, stepSize);
mitk::Vector3D movementVector;
movementVector.Fill(0.0);
movementVector.SetElement(0, -stepSize);
this->TranslateGeometry(movementVector, this->GetUpdatedTimeGeometry(interactionEvent));
}
void mitk::AffineBaseDataInteractor3D::TranslateRightKey(mitk::StateMachineAction *,
mitk::InteractionEvent *interactionEvent)
{
float stepSize = 1.0f;
this->GetDataNode()->GetFloatProperty(translationStepSizePropertyName, stepSize);
mitk::Vector3D movementVector;
movementVector.Fill(0.0);
movementVector.SetElement(0, stepSize);
this->TranslateGeometry(movementVector, this->GetUpdatedTimeGeometry(interactionEvent));
}
void mitk::AffineBaseDataInteractor3D::TranslateUpModifierKey(mitk::StateMachineAction *,
mitk::InteractionEvent *interactionEvent)
{
float stepSize = 1.0f;
this->GetDataNode()->GetFloatProperty(translationStepSizePropertyName, stepSize);
mitk::Vector3D movementVector;
movementVector.Fill(0.0);
movementVector.SetElement(1, stepSize);
this->TranslateGeometry(movementVector, this->GetUpdatedTimeGeometry(interactionEvent));
}
void mitk::AffineBaseDataInteractor3D::TranslateDownModifierKey(mitk::StateMachineAction *,
mitk::InteractionEvent *interactionEvent)
{
float stepSize = 1.0f;
this->GetDataNode()->GetFloatProperty(translationStepSizePropertyName, stepSize);
mitk::Vector3D movementVector;
movementVector.Fill(0.0);
movementVector.SetElement(1, -stepSize);
this->TranslateGeometry(movementVector, this->GetUpdatedTimeGeometry(interactionEvent));
}
void mitk::AffineBaseDataInteractor3D::RotateUpKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent)
{
float stepSize = 1.0f;
this->GetDataNode()->GetFloatProperty(rotationStepSizePropertyName, stepSize);
this->RotateGeometry(-stepSize, 0, this->GetUpdatedTimeGeometry(interactionEvent));
}
void mitk::AffineBaseDataInteractor3D::RotateDownKey(mitk::StateMachineAction *,
mitk::InteractionEvent *interactionEvent)
{
float stepSize = 1.0f;
this->GetDataNode()->GetFloatProperty(rotationStepSizePropertyName, stepSize);
this->RotateGeometry(stepSize, 0, this->GetUpdatedTimeGeometry(interactionEvent));
return;
}
void mitk::AffineBaseDataInteractor3D::RotateLeftKey(mitk::StateMachineAction *,
mitk::InteractionEvent *interactionEvent)
{
float stepSize = 1.0f;
this->GetDataNode()->GetFloatProperty(rotationStepSizePropertyName, stepSize);
this->RotateGeometry(-stepSize, 2, this->GetUpdatedTimeGeometry(interactionEvent));
}
void mitk::AffineBaseDataInteractor3D::RotateRightKey(mitk::StateMachineAction *,
mitk::InteractionEvent *interactionEvent)
{
float stepSize = 1.0f;
this->GetDataNode()->GetFloatProperty(rotationStepSizePropertyName, stepSize);
this->RotateGeometry(stepSize, 2, this->GetUpdatedTimeGeometry(interactionEvent));
}
void mitk::AffineBaseDataInteractor3D::RotateUpModifierKey(mitk::StateMachineAction *,
mitk::InteractionEvent *interactionEvent)
{
float stepSize = 1.0f;
this->GetDataNode()->GetFloatProperty(rotationStepSizePropertyName, stepSize);
this->RotateGeometry(stepSize, 1, this->GetUpdatedTimeGeometry(interactionEvent));
}
void mitk::AffineBaseDataInteractor3D::RotateDownModifierKey(mitk::StateMachineAction *,
mitk::InteractionEvent *interactionEvent)
{
float stepSize = 1.0f;
this->GetDataNode()->GetFloatProperty(rotationStepSizePropertyName, stepSize);
this->RotateGeometry(-stepSize, 1, this->GetUpdatedTimeGeometry(interactionEvent));
}
void mitk::AffineBaseDataInteractor3D::ScaleUpKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent)
{
float stepSize = 0.1f;
this->GetDataNode()->GetFloatProperty(scaleStepSizePropertyName, stepSize);
mitk::Point3D newScale;
newScale.Fill(stepSize);
this->ScaleGeometry(newScale, this->GetUpdatedTimeGeometry(interactionEvent));
}
void mitk::AffineBaseDataInteractor3D::ScaleDownKey(mitk::StateMachineAction *,
mitk::InteractionEvent *interactionEvent)
{
float stepSize = 0.1f;
this->GetDataNode()->GetFloatProperty(scaleStepSizePropertyName, stepSize);
mitk::Point3D newScale;
newScale.Fill(-stepSize);
this->ScaleGeometry(newScale, this->GetUpdatedTimeGeometry(interactionEvent));
}
void mitk::AffineBaseDataInteractor3D::ScaleGeometry(mitk::Point3D newScale, mitk::BaseGeometry *geometry)
{
mitk::Point3D anchorPoint;
float pointX = 0.0f;
float pointY = 0.0f;
float pointZ = 0.0f;
anchorPoint.Fill(0.0);
this->GetDataNode()->GetFloatProperty(anchorPointX, pointX);
this->GetDataNode()->GetFloatProperty(anchorPointY, pointY);
this->GetDataNode()->GetFloatProperty(anchorPointZ, pointZ);
anchorPoint[0] = pointX;
anchorPoint[1] = pointY;
anchorPoint[2] = pointZ;
auto *doOp = new mitk::ScaleOperation(OpSCALE, newScale, anchorPoint);
geometry->ExecuteOperation(doOp);
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::AffineBaseDataInteractor3D::RotateGeometry(mitk::ScalarType angle,
int rotationaxis,
mitk::BaseGeometry *geometry)
{
mitk::Vector3D rotationAxis = geometry->GetAxisVector(rotationaxis);
float pointX = 0.0f;
float pointY = 0.0f;
float pointZ = 0.0f;
mitk::Point3D pointOfRotation;
pointOfRotation.Fill(0.0);
this->GetDataNode()->GetFloatProperty(anchorPointX, pointX);
this->GetDataNode()->GetFloatProperty(anchorPointY, pointY);
this->GetDataNode()->GetFloatProperty(anchorPointZ, pointZ);
pointOfRotation[0] = pointX;
pointOfRotation[1] = pointY;
pointOfRotation[2] = pointZ;
auto *doOp = new mitk::RotationOperation(OpROTATE, pointOfRotation, rotationAxis, angle);
geometry->ExecuteOperation(doOp);
delete doOp;
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::AffineBaseDataInteractor3D::TranslateGeometry(mitk::Vector3D translate, mitk::BaseGeometry *geometry)
{
geometry->Translate(translate);
this->GetDataNode()->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
mitk::BaseGeometry *mitk::AffineBaseDataInteractor3D::GetUpdatedTimeGeometry(mitk::InteractionEvent *interactionEvent)
{
// Get the correct time geometry to support 3D + t
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
BaseGeometry *geometry = this->GetDataNode()->GetData()->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep);
if (geometry == nullptr)
MITK_ERROR << "Geometry is nullptr. Cannot modify it.";
return geometry;
}
void mitk::AffineBaseDataInteractor3D::DataNodeChanged()
{
mitk::DataNode::Pointer newInputNode = this->GetDataNode();
if (newInputNode.IsNotNull())
{
// add default properties
newInputNode->AddProperty(selectedColorPropertyName, mitk::ColorProperty::New(0.0, 1.0, 0.0));
newInputNode->AddProperty(deselectedColorPropertyName, mitk::ColorProperty::New(0.0, 0.0, 1.0));
newInputNode->AddProperty(translationStepSizePropertyName, mitk::FloatProperty::New(1.0f));
newInputNode->AddProperty(rotationStepSizePropertyName, mitk::FloatProperty::New(1.0f));
newInputNode->AddProperty(scaleStepSizePropertyName, mitk::FloatProperty::New(0.1f));
// save the previous color of the node, in order to restore it after the interactor is destroyed
mitk::ColorProperty::Pointer priorColor = dynamic_cast<mitk::ColorProperty *>(newInputNode->GetProperty("color"));
if (priorColor.IsNotNull())
{
mitk::ColorProperty::Pointer tmpCopyOfPriorColor = mitk::ColorProperty::New();
tmpCopyOfPriorColor->SetColor(priorColor->GetColor());
newInputNode->AddProperty(priorPropertyName, tmpCopyOfPriorColor);
}
newInputNode->SetColor(0.0, 0.0, 1.0);
}
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::AffineBaseDataInteractor3D::SetDataNode(DataNode *node)
{
this->RestoreNodeProperties(); // if there was another node set, restore it's color
DataInteractor::SetDataNode(node);
}
bool mitk::AffineBaseDataInteractor3D::CheckOverObject(const InteractionEvent *interactionEvent)
{
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return false;
Point3D currentWorldPoint;
if (interactionEvent->GetSender()->PickObject(positionEvent->GetPointerPositionOnScreen(), currentWorldPoint) ==
this->GetDataNode())
return true;
return false;
}
void mitk::AffineBaseDataInteractor3D::SelectObject(StateMachineAction *, InteractionEvent *)
{
DataNode::Pointer node = this->GetDataNode();
if (node.IsNull())
return;
mitk::ColorProperty::Pointer selectedColor =
dynamic_cast<mitk::ColorProperty *>(node->GetProperty(selectedColorPropertyName));
if (selectedColor.IsNotNull())
{
node->GetPropertyList()->SetProperty("color", selectedColor);
}
RenderingManager::GetInstance()->RequestUpdateAll();
return;
}
void mitk::AffineBaseDataInteractor3D::DeselectObject(StateMachineAction *, InteractionEvent *)
{
DataNode::Pointer node = this->GetDataNode();
if (node.IsNull())
return;
mitk::ColorProperty::Pointer selectedColor =
dynamic_cast<mitk::ColorProperty *>(node->GetProperty(deselectedColorPropertyName));
if (selectedColor.IsNotNull())
{
node->GetPropertyList()->SetProperty("color", selectedColor);
}
RenderingManager::GetInstance()->RequestUpdateAll();
return;
}
void mitk::AffineBaseDataInteractor3D::InitTranslate(StateMachineAction *, InteractionEvent *interactionEvent)
{
InitMembers(interactionEvent);
}
void mitk::AffineBaseDataInteractor3D::InitRotate(StateMachineAction *, InteractionEvent *interactionEvent)
{
InitMembers(interactionEvent);
}
bool mitk::AffineBaseDataInteractor3D::InitMembers(InteractionEvent *interactionEvent)
{
auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return false;
m_InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen();
m_InitialPickedWorldPoint = positionEvent->GetPositionInWorld();
// Get the timestep to also support 3D+t
int timeStep = 0;
if ((interactionEvent->GetSender()) != nullptr)
timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
// Make deep copy of current Geometry3D of the plane
this->GetDataNode()->GetData()->UpdateOutputInformation(); // make sure that the Geometry is up-to-date
m_OriginalGeometry =
static_cast<Geometry3D *>(this->GetDataNode()->GetData()->GetGeometry(timeStep)->Clone().GetPointer());
return true;
}
void mitk::AffineBaseDataInteractor3D::TranslateObject(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
Point3D currentPickedPoint = positionEvent->GetPositionInWorld();
Vector3D interactionMove;
interactionMove[0] = currentPickedPoint[0] - m_InitialPickedWorldPoint[0];
interactionMove[1] = currentPickedPoint[1] - m_InitialPickedWorldPoint[1];
interactionMove[2] = currentPickedPoint[2] - m_InitialPickedWorldPoint[2];
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
mitk::BaseGeometry::Pointer geometry =
this->GetDataNode()->GetData()->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep);
geometry->SetOrigin(m_OriginalGeometry->GetOrigin());
this->TranslateGeometry(interactionMove, this->GetUpdatedTimeGeometry(interactionEvent));
return;
}
void mitk::AffineBaseDataInteractor3D::RotateObject(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
Point2D currentPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen();
Point3D currentWorldPoint = positionEvent->GetPositionInWorld();
vtkCamera *camera = nullptr;
vtkRenderer *currentVtkRenderer = nullptr;
if ((interactionEvent->GetSender()) != nullptr)
{
camera = interactionEvent->GetSender()->GetVtkRenderer()->GetActiveCamera();
currentVtkRenderer = interactionEvent->GetSender()->GetVtkRenderer();
}
if (camera && currentVtkRenderer)
{
double vpn[3];
camera->GetViewPlaneNormal(vpn);
Vector3D viewPlaneNormal;
viewPlaneNormal[0] = vpn[0];
viewPlaneNormal[1] = vpn[1];
viewPlaneNormal[2] = vpn[2];
Vector3D interactionMove;
interactionMove[0] = currentWorldPoint[0] - m_InitialPickedWorldPoint[0];
interactionMove[1] = currentWorldPoint[1] - m_InitialPickedWorldPoint[1];
interactionMove[2] = currentWorldPoint[2] - m_InitialPickedWorldPoint[2];
if (interactionMove[0] == 0 && interactionMove[1] == 0 && interactionMove[2] == 0)
return;
Vector3D rotationAxis = itk::CrossProduct(viewPlaneNormal, interactionMove);
rotationAxis.Normalize();
int *size = currentVtkRenderer->GetSize();
double l2 = (currentPickedDisplayPoint[0] - m_InitialPickedDisplayPoint[0]) *
(currentPickedDisplayPoint[0] - m_InitialPickedDisplayPoint[0]) +
(currentPickedDisplayPoint[1] - m_InitialPickedDisplayPoint[1]) *
(currentPickedDisplayPoint[1] - m_InitialPickedDisplayPoint[1]);
double rotationAngle = 360.0 * sqrt(l2 / (size[0] * size[0] + size[1] * size[1]));
// Use center of data bounding box as center of rotation
Point3D rotationCenter = m_OriginalGeometry->GetCenter();
int timeStep = 0;
if ((interactionEvent->GetSender()) != nullptr)
timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
// Reset current Geometry3D to original state (pre-interaction) and
// apply rotation
RotationOperation op(OpROTATE, rotationCenter, rotationAxis, rotationAngle);
Geometry3D::Pointer newGeometry = static_cast<Geometry3D *>(m_OriginalGeometry->Clone().GetPointer());
newGeometry->ExecuteOperation(&op);
mitk::TimeGeometry::Pointer timeGeometry = this->GetDataNode()->GetData()->GetTimeGeometry();
if (timeGeometry.IsNotNull())
timeGeometry->SetTimeStepGeometry(newGeometry, timeStep);
RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void mitk::AffineBaseDataInteractor3D::ScaleObject(StateMachineAction *, InteractionEvent * /*interactionEvent*/)
{
return;
}
void mitk::AffineBaseDataInteractor3D::RestoreNodeProperties()
{
mitk::DataNode::Pointer inputNode = this->GetDataNode();
if (inputNode.IsNull())
return;
mitk::ColorProperty::Pointer color = dynamic_cast<mitk::ColorProperty *>(inputNode->GetProperty(priorPropertyName));
if (color.IsNotNull())
{
inputNode->GetPropertyList()->SetProperty("color", color);
}
inputNode->GetPropertyList()->DeleteProperty(selectedColorPropertyName);
inputNode->GetPropertyList()->DeleteProperty(deselectedColorPropertyName);
inputNode->GetPropertyList()->DeleteProperty(priorPropertyName);
inputNode->GetPropertyList()->DeleteProperty(translationStepSizePropertyName);
inputNode->GetPropertyList()->DeleteProperty(rotationStepSizePropertyName);
inputNode->GetPropertyList()->DeleteProperty(scaleStepSizePropertyName);
// update rendering
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
diff --git a/Modules/IGT/Algorithms/mitkNavigationDataToPointSetFilter.cpp b/Modules/IGT/Algorithms/mitkNavigationDataToPointSetFilter.cpp
index 98b562bfa9..19bc8ffa15 100644
--- a/Modules/IGT/Algorithms/mitkNavigationDataToPointSetFilter.cpp
+++ b/Modules/IGT/Algorithms/mitkNavigationDataToPointSetFilter.cpp
@@ -1,247 +1,247 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkNavigationDataToPointSetFilter.h"
#include <mitkPointOperation.h>
#include <mitkInteractionConst.h>
#include <itksys/SystemTools.hxx>
mitk::NavigationDataToPointSetFilter::NavigationDataToPointSetFilter()
{
mitk::PointSet::Pointer output = mitk::PointSet::New();
this->SetNumberOfRequiredOutputs(1);
this->SetNthOutput(0, output.GetPointer());
this->SetNumberOfRequiredInputs(1);
m_OperationMode = Mode3D;
m_CurrentTimeStep = 0;
m_RingBufferSize = 50; //the default ring buffer size
m_NumberForMean = 100;
}
mitk::NavigationDataToPointSetFilter::~NavigationDataToPointSetFilter()
{
}
void mitk::NavigationDataToPointSetFilter::GenerateData()
{
switch (m_OperationMode)
{
case Mode3D:
GenerateDataMode3D();
break;
case Mode3DMean:
GenerateDataMode3DMean();
break;
case Mode4D:
GenerateDataMode4D();
break;
default:
break;
}
}
void mitk::NavigationDataToPointSetFilter::SetInput(const NavigationData* nd)
{
// Process object is not const-correct so the const_cast is required here
this->ProcessObject::SetNthInput(0, const_cast<NavigationData*>(nd));
this->CreateOutputsForAllInputs();
}
void mitk::NavigationDataToPointSetFilter::SetInput(unsigned int idx, const NavigationData* nd)
{
// Process object is not const-correct so the const_cast is required here
this->ProcessObject::SetNthInput(idx, const_cast<NavigationData*>(nd));
this->CreateOutputsForAllInputs();
}
const mitk::NavigationData* mitk::NavigationDataToPointSetFilter::GetInput( void )
{
if (this->GetNumberOfInputs() < 1)
return nullptr;
return static_cast<const NavigationData*>(this->ProcessObject::GetInput(0));
}
const mitk::NavigationData* mitk::NavigationDataToPointSetFilter::GetInput( unsigned int idx )
{
if (this->GetNumberOfInputs() < 1)
return nullptr;
return static_cast<const NavigationData*>(this->ProcessObject::GetInput(idx));
}
void mitk::NavigationDataToPointSetFilter::CreateOutputsForAllInputs()
{
switch (m_OperationMode)
{
case Mode3D:
this->SetNumberOfIndexedOutputs(this->GetNumberOfIndexedInputs()); // create one pointset output for each navigation data input
break;
case Mode3DMean:
this->SetNumberOfIndexedOutputs(this->GetNumberOfIndexedInputs()); // create one pointset output for each navigation data input
break;
case Mode4D:
this->SetNumberOfIndexedOutputs(1); // create just one output pointset that will contain all input navigation data objects
break;
default:
break;
}
for (unsigned int idx = 0; idx < this->GetNumberOfIndexedOutputs(); ++idx)
{
if (this->GetOutput(idx) == nullptr)
{
DataObjectPointer newOutput = this->MakeOutput(idx);
this->SetNthOutput(idx, newOutput);
}
}
this->Modified();
}
void mitk::NavigationDataToPointSetFilter::GenerateDataMode3D()
{
for (unsigned int i = 0; i < this->GetNumberOfIndexedOutputs() ; ++i) // for each output PointSet
{
mitk::PointSet* output = this->GetOutput(i);
assert(output);
const mitk::NavigationData* input = this->GetInput(i);
assert(input);
if (input->IsDataValid() == false) // don't add point if input is invalid
continue;
mitk::PointSet::PointType pos = input->GetPosition(); // NavigationData::PositionType must be compatible with PointSet::PointType!
output->InsertPoint(output->GetSize(), pos); // add point with current position of input NavigationData to the output PointSet
// \TODO: regard ringbuffersize
}
}
/**
* @brief read n times all connected inputs and sum them into outputs. Finish with dividing each output by n.
**/
void mitk::NavigationDataToPointSetFilter::GenerateDataMode3DMean()
{
//make it editable through a Set method if needed
//check for outputs and inputs
for (unsigned int i = 0; i < this->GetNumberOfIndexedOutputs() ; ++i) // for each output PointSet; change through pointsets to collect all navigation data in order
{
assert(this->GetOutput(i));
assert(this->GetInput(i));
}
//vector of counters for each output
std::vector<unsigned int> counterVec(this->GetNumberOfIndexedOutputs(),0);
//vector of old timesteps for each output
std::vector<mitk::NavigationData::TimeStampType> vectorOldTime(this->GetNumberOfIndexedOutputs());
//use first Output to get the size of the pointsets. All output pointssets have to have the same size!
mitk::PointSet::PointIdentifier newPointId = this->GetOutput()->GetSize();
bool numberForMean_is_reached = false;
while (!numberForMean_is_reached)
{
for (unsigned int i = 0; i < this->GetNumberOfIndexedOutputs() ; ++i) // for each output PointSet; change through pointsets to collect all navigation data in order
{
mitk::PointSet* output = this->GetOutput(i);
const mitk::NavigationData* input = this->GetInput(i);
if (input->IsDataValid() == false) // don't add point if input is invalid
continue;//do not store
mitk::PointSet::PointType pos;
if (counterVec[i] == 0) //first Element must be inserted
{
vectorOldTime[i] = input->GetIGTTimeStamp();
//no need to call an update
pos = input->GetPosition(); // NavigationData::PositionType must be compatible with PointSet::PointType!
output->InsertPoint(newPointId, pos); // add point with current position of input NavigationData to the output PointSet
counterVec[i]++;
}
else
{
//manually call an update to track new positions
this->ProcessObject::GetInput(i)->Update();
input = this->GetInput(i);
mitk::NavigationData::TimeStampType newTime = input->GetIGTTimeStamp();
if (vectorOldTime[i]<newTime)
{
pos = input->GetPosition(); // NavigationData::PositionType must be compatible with PointSet::PointType!
//calculate the summ of the old position and the current coordinate
- mitk::Vector3D vec;
- vec.SetVnlVector(pos.GetVnlVector());
+ mitk::Vector3D vec(0.0);
+ vec.SetVnlVector(pos.GetVnlVector().as_ref());
mitk::PointSet::PointType oPoint = output->GetPoint(newPointId);
oPoint += vec;//summ up
output->SetPoint(newPointId, oPoint);
//store in counterVec to know how many have been added (and not skipped because of invalid data)
counterVec[i]++;
vectorOldTime[i] = newTime;
}
}
// \TODO: regard ringbuffersize
}
numberForMean_is_reached = true;
for (unsigned int i = 0; i < this->GetNumberOfIndexedOutputs() ; ++i)
{
if (counterVec[i]<m_NumberForMean)
numberForMean_is_reached = false;
}
}
//divide with counterVec
for (unsigned int i = 0; i < this->GetNumberOfIndexedOutputs() ; ++i) // for each output PointSet; change through pointsets to collect all navigation data in order
{
mitk::PointSet* output = this->GetOutput(i);
mitk::PointSet::PointType oPoint = output->GetPoint(newPointId);
for (unsigned int index = 0; index < oPoint.Size(); index++)
oPoint[index] = oPoint[index] / counterVec[i];
output->SetPoint(newPointId, oPoint);
MBI_INFO << "For output # " << i << ", " << counterVec[i] << " tracked positions used for averaging";
}
}
void mitk::NavigationDataToPointSetFilter::GenerateDataMode4D()
{
mitk::PointSet* output = this->GetOutput();
assert(output);
for (unsigned int index = 0; index < this->GetNumberOfIndexedInputs(); index++)
{
const mitk::NavigationData* nd = GetInput(index);
assert(nd);
mitk::NavigationData::PositionType point = nd->GetPosition(); //get the position
output->SetPoint( index, point, m_CurrentTimeStep); //store it in the pointset always at the current time step
}
if (m_CurrentTimeStep == m_RingBufferSize - 1) // update ring buffer index
m_CurrentTimeStep = 0;
else
m_CurrentTimeStep++;
}
void mitk::NavigationDataToPointSetFilter::SetOperationMode( OperationMode mode )
{
m_OperationMode = mode;
//Initialize 4D Mode
if (m_OperationMode == Mode4D)
m_CurrentTimeStep = 0;
this->Modified();
this->CreateOutputsForAllInputs();
}
diff --git a/Modules/IGT/Algorithms/mitkPivotCalibration.cpp b/Modules/IGT/Algorithms/mitkPivotCalibration.cpp
index 47a70d0f7a..765d1488cd 100644
--- a/Modules/IGT/Algorithms/mitkPivotCalibration.cpp
+++ b/Modules/IGT/Algorithms/mitkPivotCalibration.cpp
@@ -1,110 +1,110 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkPivotCalibration.h"
#include "vnl/algo/vnl_svd.h"
#include "vnl/vnl_matrix.h"
#include "vnl/vnl_vector.h"
#include <vtkMatrix4x4.h>
mitk::PivotCalibration::PivotCalibration() : m_NavigationDatas(std::vector<mitk::NavigationData::Pointer>()), m_ResultPivotPoint(mitk::Point3D(0.0))
{
}
mitk::PivotCalibration::~PivotCalibration()
{
}
void mitk::PivotCalibration::AddNavigationData(mitk::NavigationData::Pointer data)
{
m_NavigationDatas.push_back(data);
}
bool mitk::PivotCalibration::ComputePivotResult()
{
return ComputePivotPoint();
}
bool mitk::PivotCalibration::ComputePivotPoint()
{
double defaultThreshold = 1e-1;
std::vector<mitk::NavigationData::Pointer> _CheckedTransforms;
for (size_t i = 0; i < m_NavigationDatas.size(); ++i)
{
if (!m_NavigationDatas.at(i)->IsDataValid())
{
MITK_WARN << "Skipping invalid transform " << i << ".";
continue;
}
_CheckedTransforms.push_back(m_NavigationDatas.at(i));
}
if (_CheckedTransforms.empty())
{
MITK_WARN << "Checked Transforms are empty";
return false;
}
unsigned int rows = 3 * _CheckedTransforms.size();
unsigned int columns = 6;
vnl_matrix< double > A(rows, columns), minusI(3, 3, 0), R(3, 3);
vnl_vector< double > b(rows), x(columns), t(3);
minusI(0, 0) = -1;
minusI(1, 1) = -1;
minusI(2, 2) = -1;
//do the computation and set the internal variables
unsigned int currentRow = 0;
for (size_t i = 0; i < _CheckedTransforms.size(); ++i)
{
t = _CheckedTransforms.at(i)->GetPosition().GetVnlVector();// t = the current position of the tracked sensor
t *= -1;
b.update(t, currentRow); //b = combines the position for each collected transform in one column vector
- R = _CheckedTransforms.at(i)->GetOrientation().rotation_matrix_transpose().transpose(); // R = the current rotation of the tracked sensor, *rotation_matrix_transpose().transpose() is used to obtain original matrix
+ R = _CheckedTransforms.at(i)->GetOrientation().rotation_matrix_transpose().transpose().as_ref(); // R = the current rotation of the tracked sensor, *rotation_matrix_transpose().transpose() is used to obtain original matrix
A.update(R, currentRow, 0); //A = the matrix which stores the rotations for each collected transform and -I
A.update(minusI, currentRow, 3);
currentRow += 3;
}
vnl_svd<double> svdA(A); //The singular value decomposition of matrix A
svdA.zero_out_absolute(defaultThreshold);
//there is a solution only if rank(A)=6 (columns are linearly
//independent)
if (svdA.rank() < 6)
{
MITK_WARN << "svdA.rank() < 6";
return false;
}
else
{
x = svdA.solve(b); //x = the resulting pivot point
m_ResultRMSError = (A * x - b).rms(); //the root mean sqaure error of the computation
//sets the Pivot Point
m_ResultPivotPoint[0] = x[0];
m_ResultPivotPoint[1] = x[1];
m_ResultPivotPoint[2] = x[2];
}
return true;
}
diff --git a/Modules/IGT/IO/mitkNavigationDataPlayer.h b/Modules/IGT/IO/mitkNavigationDataPlayer.h
index ff629034dc..61e2170214 100644
--- a/Modules/IGT/IO/mitkNavigationDataPlayer.h
+++ b/Modules/IGT/IO/mitkNavigationDataPlayer.h
@@ -1,99 +1,97 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKNavigationDataPlayer_H_HEADER_INCLUDED_
#define MITKNavigationDataPlayer_H_HEADER_INCLUDED_
#include <mitkNavigationDataPlayerBase.h>
-#include <itkMultiThreader.h>
-
namespace mitk {
/**Documentation
* \brief This class is used to play recorded (see mitkNavigationDataRecorder class) NavigationDataSets.
*
* TODO
*
*
* \ingroup IGT
*/
class MITKIGT_EXPORT NavigationDataPlayer : public NavigationDataPlayerBase
{
public:
mitkClassMacro(NavigationDataPlayer, NavigationDataPlayerBase);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
enum PlayerState { PlayerStopped, PlayerRunning, PlayerPaused };
typedef mitk::NavigationData::TimeStampType TimeStampType;
/**
* \brief Used for pipeline update just to tell the pipeline that we always have to update
*/
void UpdateOutputInformation() override;
/**
* \brief This method starts the player.
*
* The method mitk::NavigationDataPlayer::SetNavigationDataSet() has to be called before.
*
* @throw mitk::IGTException If m_NavigationDataSet is null.
*/
void StartPlaying();
/**
* \brief Stops the player and closes the stream.
* After a call of StopPlaying(), StartPlaying() must be called to get new
* output data.
*/
void StopPlaying();
/**
* \brief This method pauses the player. If you want to play again call Resume()
*/
void Pause();
/**
* \brief This method resumes the player when it was paused.
*/
void Resume();
PlayerState GetCurrentPlayerState();
TimeStampType GetTimeStampSinceStart();
protected:
NavigationDataPlayer();
~NavigationDataPlayer() override;
/**
* \brief Set outputs to the navigation data object corresponding to current time.
*/
void GenerateData() override;
PlayerState m_CurPlayerState;
/**
* \brief The start time of the playing. Set in the method mitk::NavigationDataPlayer::StartPlaying().
*/
TimeStampType m_StartPlayingTimeStamp;
/**
* \brief Stores the time when a pause began.
*/
TimeStampType m_PauseTimeStamp;
TimeStampType m_TimeStampSinceStart;
};
} // namespace mitk
#endif /* MITKNavigationDataPlayer_H_HEADER_INCLUDED_ */
diff --git a/Modules/IGT/Rendering/mitkNavigationDataSliceVisualization.cpp b/Modules/IGT/Rendering/mitkNavigationDataSliceVisualization.cpp
index 19d87f5c38..0f1161ce22 100644
--- a/Modules/IGT/Rendering/mitkNavigationDataSliceVisualization.cpp
+++ b/Modules/IGT/Rendering/mitkNavigationDataSliceVisualization.cpp
@@ -1,219 +1,219 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkNavigationDataSliceVisualization.h"
#include "mitkBaseRenderer.h"
mitk::NavigationDataSliceVisualization::NavigationDataSliceVisualization() : mitk::NavigationDataToNavigationDataFilter(),
m_Renderer(nullptr),
m_ViewDirection(Axial)
{
m_TipOffset[0] = 0.0f;
m_TipOffset[1] = 0.0f;
m_TipOffset[2] = 0.0f;
m_ToolTrajectory[0] = 0;
m_ToolTrajectory[1] = 0;
m_ToolTrajectory[2] = -1;
m_WorldVerticalVector[0] = 0.0;
m_WorldVerticalVector[1] = 1.0;
m_WorldVerticalVector[2] = 0.0;
}
void mitk::NavigationDataSliceVisualization::SetToolTrajectory(Vector3D direction)
{
if (Equal(direction.GetNorm(), 0.0))
{
MITK_WARN << "Ignoring invalid direction of projection: " << direction;
return;
}
if (m_ToolTrajectory != direction)
{
m_ToolTrajectory = direction;
this->SetViewDirection(Oblique);
this->Modified();
}
}
void mitk::NavigationDataSliceVisualization::GenerateData()
{
// check if renderer was set
if (m_Renderer.IsNull())
{
itkExceptionMacro(<< "Renderer was not properly set");
}
/* update outputs with tracking data from tools */
unsigned int numberOfInputs = this->GetNumberOfInputs();
if (numberOfInputs == 0)
{
return;
}
for (unsigned int i = 0; i < numberOfInputs ; ++i)
{
NavigationData* output = this->GetOutput(i);
assert(output);
const NavigationData* input = this->GetInput(i);
assert(input);
if (!input->IsDataValid())
continue;
output->Graft(input); // First, copy all information from input to output
}
// Nothing left to do if we don't have an input with valid data
if (numberOfInputs == 0 || !this->GetInput()->IsDataValid())
return;
// get position from NavigationData to move the slice to this position
Point3D slicePosition = this->GetInput()->GetPosition();
{
NavigationData::OrientationType orientation = this->GetInput()->GetOrientation();
Vector3D transformedTipOffset;
- transformedTipOffset.SetVnlVector(orientation.rotate(m_TipOffset.GetVnlVector()));
+ transformedTipOffset.SetVnlVector(orientation.rotate(m_TipOffset.GetVnlVector()).as_ref());
slicePosition += transformedTipOffset;
mitk::SliceNavigationController::Pointer snc = m_Renderer->GetSliceNavigationController();
if (Axial == m_ViewDirection)
{
snc->SetViewDirection(mitk::SliceNavigationController::Axial);
snc->SelectSliceByPoint(slicePosition);
}
else if (Sagittal == m_ViewDirection)
{
snc->SetViewDirection(mitk::SliceNavigationController::Sagittal);
snc->SelectSliceByPoint(slicePosition);
}
else if (Frontal == m_ViewDirection)
{
snc->SetViewDirection(mitk::SliceNavigationController::Frontal);
snc->SelectSliceByPoint(slicePosition);
}
else if (AxialOblique == m_ViewDirection || SagittalOblique == m_ViewDirection)
{
const int slicingPlaneXAxis = AxialOblique == m_ViewDirection ? 0 : 2;
// The column 0 is the slicing plane's x-axis, column 1 is the slicing plane's y-axis
const mitk::PlaneGeometry::TransformType::MatrixType &m =
m_Renderer->GetCurrentWorldPlaneGeometry()->GetIndexToWorldTransform()->GetMatrix();
// Rotate the tool trajectory vector into world coordinate frame (assuming
// NavigationData has passed through a NavigationDataTransformFilter to
// convert it into world coordinate frame)
Vector3D slicingPlaneYAxisVector;
- slicingPlaneYAxisVector.SetVnlVector(orientation.rotate(m_ToolTrajectory.GetVnlVector()));
+ slicingPlaneYAxisVector.SetVnlVector(orientation.rotate(m_ToolTrajectory.GetVnlVector()).as_ref());
// Project the tool trajectory onto the plane normal to x-axis of this
// oblique slicing. This defines the y-axis ("up") of the oblique slicing
// plane
slicingPlaneYAxisVector[slicingPlaneXAxis] = 0.0;
// Do nothing for ambigous/undefined cases:
// - the R-L component of the x-axis is zero (for AxialOblique)
// - the S-I component of the x-axis is zero (for SagittalOblique)
// - the A-P component of the y-axis is zero
if ( m(slicingPlaneXAxis,0) == 0.0 ||
m(1,1) == 0.0 ||
(slicingPlaneXAxis != 0 && slicingPlaneYAxisVector[0] == 0.0) ||
(slicingPlaneXAxis != 1 && slicingPlaneYAxisVector[1] == 0.0) ||
(slicingPlaneXAxis != 2 && slicingPlaneYAxisVector[2] == 0.0) )
{
return;
}
// Maintain the A-P orientation of the slice's y-axis regardless of what
// direction the tool trajectory points
/// @todo<C++11> Use std::signbit
if ( (m(1,1) > 0) != (slicingPlaneYAxisVector[1] > 0) )
{
slicingPlaneYAxisVector *= -1;
}
Vector3D slicingPlaneXAxisVector;
slicingPlaneXAxisVector.Fill(0.0);
// For AxialOblique: maintain the Left/Right direction of the slice's x-axis
// For SagittalOblique: maintain the Superior/Inferior direction of the slice's x-axis
/// @todo<C++11> Use std::copysign
slicingPlaneXAxisVector[slicingPlaneXAxis] = m(slicingPlaneXAxis,0) > 0 ? 1.0 : -1.0;
Point3D origin;
FillVector3D(origin, 0.0, 0.0, 0.0);
snc->ReorientSlices(origin, slicingPlaneXAxisVector, slicingPlaneYAxisVector);
snc->SelectSliceByPoint(slicePosition);
}
else if (Oblique == m_ViewDirection)
{
Vector3D slicingPlaneNormalVector;
- slicingPlaneNormalVector.SetVnlVector(orientation.rotate(m_ToolTrajectory.GetVnlVector()));
+ slicingPlaneNormalVector.SetVnlVector(orientation.rotate(m_ToolTrajectory.GetVnlVector()).as_ref());
// The second column of the Index-to-World matrix is the positive y-axis
// of the current slicing plane in world coordinates.
const mitk::PlaneGeometry::TransformType::MatrixType &m =
m_Renderer->GetCurrentWorldPlaneGeometry()->GetIndexToWorldTransform()->GetMatrix();
mitk::Vector3D currentSlicingPlaneUpVector;
mitk::FillVector3D(currentSlicingPlaneUpVector, m[0][1], m[1][1], m[2][1]);
mitk::Vector3D worldUpVector = m_WorldVerticalVector;
if (angle(worldUpVector.GetVnlVector(), currentSlicingPlaneUpVector.GetVnlVector()) > vnl_math::pi_over_2 )
{
worldUpVector *= -1;
}
mitk::PlaneGeometry::Pointer slicingPlane = mitk::PlaneGeometry::New();
Point3D origin;
FillVector3D(origin, 0.0, 0.0, 0.0);
slicingPlane->InitializePlane(origin, slicingPlaneNormalVector);
// Now that we have the direction of WorldVerticalVector chosen to be the
// most "up" direction, project it onto the slicing plane to define the
// up vector (y-axis) of the reoriented slices
mitk::Vector3D slicingPlaneUpVector;
if ( slicingPlane->Project(worldUpVector, slicingPlaneUpVector) )
{
// slicingPlaneUpVector CROSS slicingPlaneNormalVector -> slicingPlaneRightVector
// Math is done in double precision as much as possible to get more
// orthogonal right and up vectors which fixes a VNL SVD error when
// the WorldGeometry matrix is later inverted
itk::Vector<double,3> slicingPlaneUpVector_double;
FillVector3D(slicingPlaneUpVector_double,
slicingPlaneUpVector[0], slicingPlaneUpVector[1], slicingPlaneUpVector[2]);
itk::Vector<double,3> slicingPlaneNormalVector_double;
FillVector3D(slicingPlaneNormalVector_double,
slicingPlaneNormalVector[0], slicingPlaneNormalVector[1], slicingPlaneNormalVector[2]);
itk::Vector<double,3> slicingPlaneRightVector_double = itk::CrossProduct(slicingPlaneUpVector_double,
slicingPlaneNormalVector_double);
mitk::Vector3D slicingPlaneRightVector;
mitk::FillVector3D(slicingPlaneRightVector,
slicingPlaneRightVector_double[0], slicingPlaneRightVector_double[1], slicingPlaneRightVector_double[2]);
mitk::FillVector3D(slicingPlaneUpVector,
slicingPlaneUpVector_double[0], slicingPlaneUpVector_double[1], slicingPlaneUpVector_double[2]);
snc->ReorientSlices(origin, slicingPlaneRightVector, slicingPlaneUpVector);
snc->SelectSliceByPoint(slicePosition);
}
}
else
{
MITK_ERROR << "Unsupported ViewDirection: " << m_ViewDirection;
}
m_Renderer->RequestUpdate();
}
}
diff --git a/Modules/IGT/Testing/mitkTrackingDeviceTest.cpp b/Modules/IGT/Testing/mitkTrackingDeviceTest.cpp
index 0a12699607..08ba485949 100644
--- a/Modules/IGT/Testing/mitkTrackingDeviceTest.cpp
+++ b/Modules/IGT/Testing/mitkTrackingDeviceTest.cpp
@@ -1,112 +1,112 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkTrackingDevice.h"
#include "mitkTestingMacros.h"
#include "mitkTrackingTool.h"
#include "mitkTrackingTypes.h"
#include "mitkCommon.h"
#include <itkObject.h>
#include <itkObjectFactory.h>
#include <usModuleContext.h>
#include <usGetModuleContext.h>
#include <usModule.h>
#include <usModuleResource.h>
#include <usModuleResourceStream.h>
#include "mitkTrackingDeviceTypeCollection.h"
#include "mitkUnspecifiedTrackingTypeInformation.h"
//All Tracking devices, which should be avaiable by default
#include "mitkNDIAuroraTypeInformation.h"
#include "mitkNDIPolarisTypeInformation.h"
#include "mitkVirtualTrackerTypeInformation.h"
#include "mitkMicronTrackerTypeInformation.h"
#include "mitkNPOptitrackTrackingTypeInformation.h"
#include "mitkOpenIGTLinkTypeInformation.h"
/**
* Create new class and derive it from TrackingDevice
*/
class TrackingDeviceTestClass : public mitk::TrackingDevice
{
public:
mitkClassMacro(TrackingDeviceTestClass, mitk::TrackingDevice);
itkFactorylessNewMacro(Self)
itkCloneMacro(Self)
bool OpenConnection() override{return true;};
bool CloseConnection() override{return true;};
- bool StartTracking() override{this->SetState(Tracking); this->m_TrackingFinishedMutex->Unlock(); return true;};
+ bool StartTracking() override{this->SetState(Tracking); return true;};
mitk::TrackingTool* GetTool(unsigned int /*toolNumber*/) const override {return nullptr;};
unsigned int GetToolCount() const override {return 1;};
};
/**
* This function is testing the Class TrackingDevice. For most tests we would need the MicronTracker hardware, so only a few
* simple tests, which can run without the hardware are implemented yet (2009, January, 23rd). As soon as there is a working
* concept to test the tracking classes which are very close to the hardware on all systems more tests are needed here.
*/
int mitkTrackingDeviceTest(int /* argc */, char* /*argv*/[])
{
MITK_TEST_BEGIN("TrackingDevice");
mitk::TrackingDeviceTypeCollection deviceTypeCollection;
deviceTypeCollection.RegisterTrackingDeviceType(new mitk::NDIAuroraTypeInformation());
deviceTypeCollection.RegisterAsMicroservice();
deviceTypeCollection.RegisterTrackingDeviceType(new mitk::VirtualTrackerTypeInformation());
deviceTypeCollection.RegisterTrackingDeviceType(new mitk::NDIPolarisTypeInformation());
deviceTypeCollection.RegisterTrackingDeviceType(new mitk::MicronTrackerTypeInformation());
// Test instantiation of TrackingDevice
TrackingDeviceTestClass::Pointer trackingDeviceTestClass = TrackingDeviceTestClass::New();
MITK_TEST_CONDITION(trackingDeviceTestClass.IsNotNull(),"Test instatiation");
// Test method GetState()
MITK_TEST_CONDITION(trackingDeviceTestClass->GetState()==mitk::TrackingDevice::Setup,"Mode should be initialized to SETUP");
// Test method SetType()
MITK_TEST_CONDITION(trackingDeviceTestClass->GetType()==mitk::UnspecifiedTrackingTypeInformation::GetTrackingDeviceName(),"Type should be initialized to 'not specified'");
trackingDeviceTestClass->SetType(mitk::NDIAuroraTypeInformation::GetTrackingDeviceName());
MITK_TEST_CONDITION(trackingDeviceTestClass->GetType() == mitk::NDIAuroraTypeInformation::GetTrackingDeviceName(), "Type should be NDIAurora, as it has just been set");
trackingDeviceTestClass->SetType(mitk::NDIPolarisTypeInformation::GetTrackingDeviceName());
MITK_TEST_CONDITION(trackingDeviceTestClass->GetType() == mitk::NDIPolarisTypeInformation::GetTrackingDeviceName(), "Type should be NDIPolaris, as it has just been set");
trackingDeviceTestClass->SetType(mitk::MicronTrackerTypeInformation::GetTrackingDeviceName());
MITK_TEST_CONDITION(trackingDeviceTestClass->GetType() == mitk::MicronTrackerTypeInformation::GetTrackingDeviceName(), "Type should be ClaronMicron, as it has just been set");
trackingDeviceTestClass->SetType(mitk::VirtualTrackerTypeInformation::GetTrackingDeviceName());
MITK_TEST_CONDITION(trackingDeviceTestClass->GetType() == mitk::VirtualTrackerTypeInformation::GetTrackingDeviceName(), "Type should be VirtualTracker, as it has just been set");
// Test method StopTracking()
trackingDeviceTestClass->StartTracking();
trackingDeviceTestClass->StopTracking();
MITK_TEST_CONDITION(trackingDeviceTestClass->GetState()== mitk::TrackingDevice::Ready,"Type should be NDIAurora, as it has just been set");
MITK_TEST_CONDITION(deviceTypeCollection.GetTrackingDeviceTypeInformation(mitk::VirtualTrackerTypeInformation::GetTrackingDeviceName())->GetTrackingDeviceName()
== mitk::VirtualTrackerTypeInformation::GetTrackingDeviceName(), "Test GetTrackingDeviceTypeInformation");
MITK_TEST_CONDITION(deviceTypeCollection.GetTrackingDeviceTypeInformation(mitk::VirtualTrackerTypeInformation::GetTrackingDeviceName())->m_TrackingDeviceData[0].Model
== "Virtual Tracker", "Test GetTrackingDeviceTypeInformation");
std::vector<std::string> names = deviceTypeCollection.GetTrackingDeviceTypeNames();
MITK_TEST_CONDITION(names[0] == mitk::NDIAuroraTypeInformation::GetTrackingDeviceName(), "Test collection name list");
MITK_TEST_CONDITION(names[1] == mitk::VirtualTrackerTypeInformation::GetTrackingDeviceName(), "Test collection name list");
MITK_TEST_CONDITION(names[2] == mitk::NDIPolarisTypeInformation::GetTrackingDeviceName(), "Test collection name list");
MITK_TEST_CONDITION(names[3] == mitk::MicronTrackerTypeInformation::GetTrackingDeviceName(), "Test collection name list");
MITK_TEST_END();
}
diff --git a/Modules/IGT/TrackingDevices/mitkClaronTool.h b/Modules/IGT/TrackingDevices/mitkClaronTool.h
index 3575653442..e642253c01 100644
--- a/Modules/IGT/TrackingDevices/mitkClaronTool.h
+++ b/Modules/IGT/TrackingDevices/mitkClaronTool.h
@@ -1,82 +1,81 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKCLARONTOOL_H_HEADER_INCLUDED_
#define MITKCLARONTOOL_H_HEADER_INCLUDED_
#include <mitkClaronInterface.h>
#include <mitkTrackingTool.h>
-#include <itkFastMutexLock.h>
namespace mitk
{
class ClaronTrackingDevice;
/** Documentation:
* \brief An object of this class represents a MicronTracker 2 tool.
* A tool has to be added to a tracking device which will then
* continuously update the tool coordinates.
* \ingroup IGT
*/
class MITKIGT_EXPORT ClaronTool : public TrackingTool
{
public:
friend class ClaronTrackingDevice;
mitkClassMacro(ClaronTool, TrackingTool);
/**
* \brief Loads a tool calibration file. Without this file the tool can not be tracked!
*/
bool LoadFile(const char* filename);
/**
* \brief Loads a tool calibration file. Without this file the tool can not be tracked!
*/
bool LoadFile(std::string filename);
std::string GetFile();
/**
* \brief Sets the handle of the tool.
* \param handle The new handle of the tool.
*/
void SetToolHandle (claronToolHandle handle);
/**
* \return Returns the calibration name which is used to identify the tool.
*/
std::string GetCalibrationName();
/**
* \brief Sets the calibration name of the tool. Be careful, only use this method if you know what you are doing.
* If you want to change the tool name use the method setToolName instead!
*/
void SetCalibrationName(std::string name);
/**
* @return Returns the tool handle of the tool.
*/
claronToolHandle GetToolHandle();
protected:
itkFactorylessNewMacro(Self);
itkCloneMacro(Self)
ClaronTool();
~ClaronTool() override;
/** \brief Tool handle variable from tracking device */
claronToolHandle m_ToolHandle;
/** \brief Variable which holds the Tool's calibration name */
std::string m_CalibrationName;
/** \brief Variable to check filename's format and to get back complete filename */
std::string m_Filename;
};
}//mitk
#endif // MITKCLARONTOOL_H_HEADER_INCLUDED_
diff --git a/Modules/IGT/TrackingDevices/mitkClaronTrackingDevice.cpp b/Modules/IGT/TrackingDevices/mitkClaronTrackingDevice.cpp
index db2ecf050c..9be11acfcb 100644
--- a/Modules/IGT/TrackingDevices/mitkClaronTrackingDevice.cpp
+++ b/Modules/IGT/TrackingDevices/mitkClaronTrackingDevice.cpp
@@ -1,327 +1,306 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkClaronTrackingDevice.h"
#include "mitkClaronTool.h"
#include "mitkIGTConfig.h"
#include "mitkIGTTimeStamp.h"
#include "mitkIGTHardwareException.h"
#include <itksys/SystemTools.hxx>
#include <iostream>
-#include <itkMutexLockHolder.h>
#include <mitkIOUtil.h>
#include <mitkMicronTrackerTypeInformation.h>
-typedef itk::MutexLockHolder<itk::FastMutexLock> MutexLockHolder;
-
mitk::ClaronTrackingDevice::ClaronTrackingDevice(): mitk::TrackingDevice()
{
//set the type of this tracking device
this->m_Data = mitk::MicronTrackerTypeInformation::GetDeviceDataMicronTrackerH40();
- this->m_MultiThreader = itk::MultiThreader::New();
- m_ThreadID = 0;
-
m_Device = mitk::ClaronInterface::New();
//############################# standard directories ##################################
if (m_Device->IsMicronTrackerInstalled())
{
m_ToolfilesDir = mitk::IOUtil::CreateTemporaryDirectory();
#ifdef MITK_MICRON_TRACKER_CALIBRATION_DIR
m_CalibrationDir = std::string(MITK_MICRON_TRACKER_CALIBRATION_DIR);
#endif
}
else
{
m_ToolfilesDir = "Error - No Microntracker installed";
m_CalibrationDir = "Error - No Microntracker installed";
}
//##################################################################################################
m_Device->Initialize(m_CalibrationDir, m_ToolfilesDir);
}
bool mitk::ClaronTrackingDevice::IsDeviceInstalled()
{
mitk::ClaronInterface::Pointer tempInterface = mitk::ClaronInterface::New();
return tempInterface->IsMicronTrackerInstalled();
}
mitk::ClaronTrackingDevice::~ClaronTrackingDevice()
{
}
mitk::TrackingTool* mitk::ClaronTrackingDevice::AddTool( const char* toolName, const char* fileName )
{
mitk::ClaronTool::Pointer t = mitk::ClaronTool::New();
if (t->LoadFile(fileName) == false)
{
return nullptr;
}
t->SetToolName(toolName);
if (this->InternalAddTool(t) == false)
return nullptr;
return t.GetPointer();
}
bool mitk::ClaronTrackingDevice::InternalAddTool(ClaronTool::Pointer tool)
{
m_AllTools.push_back(tool);
return true;
}
std::vector<mitk::ClaronTool::Pointer> mitk::ClaronTrackingDevice::DetectTools()
{
std::vector<mitk::ClaronTool::Pointer> returnValue;
std::vector<claronToolHandle> allHandles = m_Device->GetAllActiveTools();
for (auto iter = allHandles.begin(); iter != allHandles.end(); ++iter)
{
ClaronTool::Pointer newTool = ClaronTool::New();
newTool->SetToolName(m_Device->GetName(*iter));
newTool->SetCalibrationName(m_Device->GetName(*iter));
newTool->SetToolHandle(*iter);
returnValue.push_back(newTool);
}
return returnValue;
}
bool mitk::ClaronTrackingDevice::StartTracking()
{
//By Alfred: next line because no temp directory is set if MicronTracker is not installed
if (!m_Device->IsMicronTrackerInstalled())
return false;
//##################################################################################
//be sure that the temp-directory is empty at start: delete all files in the tool files directory
itksys::SystemTools::RemoveADirectory(m_ToolfilesDir.c_str());
itksys::SystemTools::MakeDirectory(m_ToolfilesDir.c_str());
//copy all toolfiles into the temp directory
for (unsigned int i=0; i<m_AllTools.size(); i++)
{
itksys::SystemTools::CopyAFile(m_AllTools[i]->GetFile().c_str(), m_ToolfilesDir.c_str());
}
this->SetState(Tracking); // go to mode Tracking
- this->m_StopTrackingMutex->Lock(); // update the local copy of m_StopTracking
+ this->m_StopTrackingMutex.lock(); // update the local copy of m_StopTracking
this->m_StopTracking = false;
- this->m_StopTrackingMutex->Unlock();
+ this->m_StopTrackingMutex.unlock();
//restart the Microntracker, so it will load the new tool files
m_Device->StopTracking();
m_Device->Initialize(m_CalibrationDir,m_ToolfilesDir);
if (m_Device->StartTracking())
{
mitk::IGTTimeStamp::GetInstance()->Start(this);
- m_ThreadID = m_MultiThreader->SpawnThread(this->ThreadStartTracking, this); // start a new thread that executes the TrackTools() method
+ m_Thread = std::thread(&ClaronTrackingDevice::ThreadStartTracking, this); // start a new thread that executes the TrackTools() method
return true;
}
else
{mitkThrowException(mitk::IGTHardwareException) << "Error while trying to start the device!";}
}
bool mitk::ClaronTrackingDevice::StopTracking()
{
Superclass::StopTracking();
//delete all files in the tool files directory
itksys::SystemTools::RemoveADirectory(m_ToolfilesDir.c_str());
return true;
}
unsigned int mitk::ClaronTrackingDevice::GetToolCount() const
{
return (unsigned int)this->m_AllTools.size();
}
mitk::TrackingTool* mitk::ClaronTrackingDevice::GetTool(unsigned int toolNumber) const
{
if ( toolNumber >= this->GetToolCount())
return nullptr;
else
return this->m_AllTools[toolNumber];
}
bool mitk::ClaronTrackingDevice::OpenConnection()
{
bool returnValue;
//Create the temp directory
itksys::SystemTools::MakeDirectory(m_ToolfilesDir.c_str());
m_Device->Initialize(m_CalibrationDir,m_ToolfilesDir);
returnValue = m_Device->StartTracking();
if (returnValue)
{
this->SetState(Ready);
}
else
{
//reset everything
if (m_Device.IsNull())
{
m_Device = mitk::ClaronInterface::New();
m_Device->Initialize(m_CalibrationDir, m_ToolfilesDir);
}
m_Device->StopTracking();
this->SetState(Setup);
mitkThrowException(mitk::IGTHardwareException) << "Error while trying to open connection to the MicronTracker.";
}
return returnValue;
}
bool mitk::ClaronTrackingDevice::CloseConnection()
{
bool returnValue = true;
if (this->GetState() == Setup)
return true;
returnValue = m_Device->StopTracking();
//delete the temporary directory
itksys::SystemTools::RemoveADirectory(m_ToolfilesDir.c_str());
this->SetState(Setup);
return returnValue;
}
mitk::ClaronInterface* mitk::ClaronTrackingDevice::GetDevice()
{
return m_Device;
}
std::vector<mitk::ClaronTool::Pointer> mitk::ClaronTrackingDevice::GetAllTools()
{
return this->m_AllTools;
}
void mitk::ClaronTrackingDevice::TrackTools()
{
try
{
/* lock the TrackingFinishedMutex to signal that the execution rights are now transfered to the tracking thread */
- MutexLockHolder trackingFinishedLockHolder(*m_TrackingFinishedMutex); // keep lock until end of scope
+ std::lock_guard<std::mutex> trackingFinishedLockHolder(m_TrackingFinishedMutex); // keep lock until end of scope
bool localStopTracking; // Because m_StopTracking is used by two threads, access has to be guarded by a mutex. To minimize thread locking, a local copy is used here
- this->m_StopTrackingMutex->Lock(); // update the local copy of m_StopTracking
+ this->m_StopTrackingMutex.lock(); // update the local copy of m_StopTracking
localStopTracking = this->m_StopTracking;
- this->m_StopTrackingMutex->Unlock();
+ this->m_StopTrackingMutex.unlock();
while ((this->GetState() == Tracking) && (localStopTracking == false))
{
this->GetDevice()->GrabFrame();
std::vector<mitk::ClaronTool::Pointer> detectedTools = this->DetectTools();
std::vector<mitk::ClaronTool::Pointer> allTools = this->GetAllTools();
std::vector<mitk::ClaronTool::Pointer>::iterator itAllTools;
for(itAllTools = allTools.begin(); itAllTools != allTools.end(); itAllTools++)
{
mitk::ClaronTool::Pointer currentTool = *itAllTools;
//test if current tool was detected
std::vector<mitk::ClaronTool::Pointer>::iterator itDetectedTools;
bool foundTool = false;
for(itDetectedTools = detectedTools.begin(); itDetectedTools != detectedTools.end(); itDetectedTools++)
{
mitk::ClaronTool::Pointer aktuDet = *itDetectedTools;
std::string tempString(currentTool->GetCalibrationName());
if (tempString.compare(aktuDet->GetCalibrationName())==0)
{
currentTool->SetToolHandle(aktuDet->GetToolHandle());
foundTool = true;
}
}
if (!foundTool)
{
currentTool->SetToolHandle(0);
}
if (currentTool->GetToolHandle() != 0)
{
currentTool->SetDataValid(true);
//get tip position of tool:
std::vector<double> pos_vector = this->GetDevice()->GetTipPosition(currentTool->GetToolHandle());
//write tip position into tool:
mitk::Point3D pos;
pos[0] = pos_vector[0];
pos[1] = pos_vector[1];
pos[2] = pos_vector[2];
currentTool->SetPosition(pos);
//get tip quaternion of tool
std::vector<double> quat = this->GetDevice()->GetTipQuaternions(currentTool->GetToolHandle());
//write tip quaternion into tool
mitk::Quaternion orientation(quat[1], quat[2], quat[3], quat[0]);
currentTool->SetOrientation(orientation);
//TODO: read the timestamp data from the tracking device interface
currentTool->SetIGTTimeStamp(mitk::IGTTimeStamp::GetInstance()->GetElapsed());
}
else
{
mitk::Point3D origin;
origin.Fill(0);
currentTool->SetPosition(origin);
currentTool->SetOrientation(mitk::Quaternion(0,0,0,0));
currentTool->SetDataValid(false);
}
}
/* Update the local copy of m_StopTracking */
- this->m_StopTrackingMutex->Lock();
+ this->m_StopTrackingMutex.lock();
localStopTracking = m_StopTracking;
- this->m_StopTrackingMutex->Unlock();
+ this->m_StopTrackingMutex.unlock();
}
}
catch(...)
{
this->StopTracking();
mitkThrowException(mitk::IGTHardwareException) << "Error while trying to track tools. Thread stopped.";
}
}
bool mitk::ClaronTrackingDevice::IsMicronTrackerInstalled()
{
return this->m_Device->IsMicronTrackerInstalled();
}
-ITK_THREAD_RETURN_TYPE mitk::ClaronTrackingDevice::ThreadStartTracking(void* pInfoStruct)
+void mitk::ClaronTrackingDevice::ThreadStartTracking()
{
- /* extract this pointer from Thread Info structure */
- struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct;
- if (pInfo == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
- if (pInfo->UserData == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
- ClaronTrackingDevice *trackingDevice = (ClaronTrackingDevice*)pInfo->UserData;
-
- if (trackingDevice != nullptr)
- trackingDevice->TrackTools();
-
- return ITK_THREAD_RETURN_VALUE;
+ this->TrackTools();
}
diff --git a/Modules/IGT/TrackingDevices/mitkClaronTrackingDevice.h b/Modules/IGT/TrackingDevices/mitkClaronTrackingDevice.h
index 64c8591528..1ab4f4daf1 100644
--- a/Modules/IGT/TrackingDevices/mitkClaronTrackingDevice.h
+++ b/Modules/IGT/TrackingDevices/mitkClaronTrackingDevice.h
@@ -1,165 +1,164 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKCLARONTRACKINGDEVICE_H_HEADER_INCLUDED_
#define MITKCLARONTRACKINGDEVICE_H_HEADER_INCLUDED_
+#include <thread>
#include <vector>
#include <mitkIGTConfig.h>
#include <mitkTrackingDevice.h>
#include <mitkClaronTool.h>
-#include <itkMultiThreader.h>
//only include MicronTracker if cmake Variable is on else the ClaronInterfaceStub is included
#ifdef MITK_USE_MICRON_TRACKER
#include <mitkClaronInterface.h>
#else
#include <mitkClaronInterfaceStub.h>
#endif
namespace mitk
{
/** Documentation:
* \brief An object of this class represents the MicronTracker device. You can add tools to this
* device, then open the connection and start tracking. The tracking device will then
* continuously update the tool coordinates.
* \ingroup IGT
*/
class MITKIGT_EXPORT ClaronTrackingDevice : public TrackingDevice
{
public:
mitkClassMacro(ClaronTrackingDevice, TrackingDevice);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* @returns Returns true if the MicronTracker is installed on this build (means activated in CMAKE). False if not.
*/
bool IsDeviceInstalled() override;
/**
* \brief Starts the tracking.
* \return Returns true if the tracking is started. Throws an exception if an error occures.
* @throw mitk::IGTHardwareException Throws an exception if there is an error during start tracking.
*/
bool StartTracking() override;
/**
* \brief Stops the tracking.
* \return Returns true if the tracking is stopped.
*/
bool StopTracking() override;
/**
* \brief Opens the connection to the device. This have to be done before the tracking is started.
* @throw mitk::IGTHardwareException Throws an exception if there is an error during open connection.
*/
bool OpenConnection() override;
/**
* \brief Closes the connection and clears all resources.
*/
bool CloseConnection() override;
/**
* \return Returns the number of tools which have been added to the device.
*/
unsigned int GetToolCount() const override;
/**
* \param toolNumber The number of the tool which should be given back.
* \return Returns the tool which the number "toolNumber". Returns nullptr, if there is
* no tool with this number.
*/
TrackingTool* GetTool(unsigned int toolNumber) const override;
/**
* \brief Create a new Claron tool with toolName and fileName and add it to the list of tools
*
* This method will create a new ClaronTool object, load the tool definition file fileName,
* set the tool name toolName and then add it to the list of tools.
* It returns a pointer of type mitk::TrackingTool to the tool
* that can be used to read tracking data from it.
* This is the only way to add tools to ClaronTrackingDevice.
*
* \warning adding tools is not possible in tracking mode, only in setup and ready.
*/
mitk::TrackingTool* AddTool(const char* toolName, const char* fileName);
/**
* \return Returns whether the MicronTracker is installed (means whether the C-Make-Variable "MITK_USE_MICRON_TRACKER" is set),
* so returns false in this case.
* \deprecatedSince{2014_03} This method is deprecated, please use the static method IsDeviceInstalled() instead.
*/
DEPRECATED(bool IsMicronTrackerInstalled());
/** @brief Sets the directory where the calibration file of the MicronTracker can be found. */
itkSetMacro(CalibrationDir,std::string);
/** @brief Gets the current calibration directory. */
itkGetMacro(CalibrationDir,std::string);
protected:
ClaronTrackingDevice();
~ClaronTrackingDevice() override;
/**
* \brief Adds a tool to the tracking device.
*
* \param tool The tool which will be added.
* \return Returns true if the tool has been added, false otherwise.
*/
bool InternalAddTool(ClaronTool::Pointer tool);
/**
* \brief This method tracks tools as long as the variable m_Mode is set to "Tracking".
* Tracking tools means grabbing frames from the camera an updating the tools.
* @throw mitk::IGTHardwareException Throws an exception if there is an error during tracking of tools.
*/
void TrackTools();
/**
* \brief Automatically detects tools in field of measurement of the tracking device.
* Tools can only be detected if their calibration file is availiable in the directory
* for calibration files.
* \return Returns all detected Tools.
*/
std::vector<ClaronTool::Pointer> DetectTools();
/**
* \return Returns all tools of the tracking device.
*/
std::vector<ClaronTool::Pointer> GetAllTools();
/**
* \return Gives back the device which is represented by an object of the class ClaronInterface.
*/
ClaronInterface* GetDevice();
- static ITK_THREAD_RETURN_TYPE ThreadStartTracking(void* data);
+ void ThreadStartTracking();
std::vector<ClaronTool::Pointer> m_AllTools; ///< vector holding all tools
ClaronInterface::Pointer m_Device; ///< represents the interface to the tracking hardware
- itk::MultiThreader::Pointer m_MultiThreader;
- int m_ThreadID;
+ std::thread m_Thread;
/** \brief The directory where the camera calibration files can be found */
std::string m_CalibrationDir;
/** \brief The directory where the tool calibration files can be found */
std::string m_ToolfilesDir;
};
}//mitk
#endif /* MITKCLARONTRACKINGDEVICE_H_HEADER_INCLUDED_ */
diff --git a/Modules/IGT/TrackingDevices/mitkNDITrackingDevice.cpp b/Modules/IGT/TrackingDevices/mitkNDITrackingDevice.cpp
index fac6bcda42..659914e77b 100644
--- a/Modules/IGT/TrackingDevices/mitkNDITrackingDevice.cpp
+++ b/Modules/IGT/TrackingDevices/mitkNDITrackingDevice.cpp
@@ -1,1349 +1,1324 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkNDITrackingDevice.h"
#include "mitkIGTTimeStamp.h"
#include "mitkIGTHardwareException.h"
#include <cstdio>
#include <itksys/SystemTools.hxx>
-#include <itkMutexLockHolder.h>
#include <mitkUnspecifiedTrackingTypeInformation.h>
#include <mitkNDIPolarisTypeInformation.h>
#include <mitkNDIAuroraTypeInformation.h>
// vtk
#include <vtkSphereSource.h>
-typedef itk::MutexLockHolder<itk::FastMutexLock> MutexLockHolder;
-
const unsigned char CR = 0xD; // == '\r' - carriage return
const unsigned char LF = 0xA; // == '\n' - line feed
mitk::NDITrackingDevice::NDITrackingDevice() :
TrackingDevice(), m_DeviceName(""), m_PortNumber(mitk::SerialCommunication::COM5), m_BaudRate(mitk::SerialCommunication::BaudRate9600),
m_DataBits(mitk::SerialCommunication::DataBits8), m_Parity(mitk::SerialCommunication::None), m_StopBits(mitk::SerialCommunication::StopBits1),
m_HardwareHandshake(mitk::SerialCommunication::HardwareHandshakeOff),
-m_IlluminationActivationRate(Hz20), m_DataTransferMode(TX), m_6DTools(), m_ToolsMutex(nullptr),
-m_SerialCommunication(nullptr), m_SerialCommunicationMutex(nullptr), m_DeviceProtocol(nullptr),
-m_MultiThreader(nullptr), m_ThreadID(0), m_OperationMode(ToolTracking6D), m_MarkerPointsMutex(nullptr), m_MarkerPoints()
+m_IlluminationActivationRate(Hz20), m_DataTransferMode(TX), m_6DTools(),
+m_SerialCommunication(nullptr), m_DeviceProtocol(nullptr),
+m_OperationMode(ToolTracking6D), m_MarkerPoints()
{
m_Data = mitk::UnspecifiedTrackingTypeInformation::GetDeviceDataUnspecified();
m_6DTools.clear();
- m_SerialCommunicationMutex = itk::FastMutexLock::New();
m_DeviceProtocol = NDIProtocol::New();
m_DeviceProtocol->SetTrackingDevice(this);
m_DeviceProtocol->UseCRCOn();
- m_MultiThreader = itk::MultiThreader::New();
- m_ToolsMutex = itk::FastMutexLock::New();
- m_MarkerPointsMutex = itk::FastMutexLock::New();
m_MarkerPoints.reserve(50); // a maximum of 50 marker positions can be reported by the tracking device
}
bool mitk::NDITrackingDevice::UpdateTool(mitk::TrackingTool* tool)
{
if (this->GetState() != Setup)
{
mitk::NDIPassiveTool* ndiTool = dynamic_cast<mitk::NDIPassiveTool*>(tool);
if (ndiTool == nullptr)
return false;
std::string portHandle = ndiTool->GetPortHandle();
//return false if the SROM Data has not been set
if (ndiTool->GetSROMData() == nullptr)
return false;
NDIErrorCode returnvalue;
returnvalue = m_DeviceProtocol->PVWR(&portHandle, ndiTool->GetSROMData(), ndiTool->GetSROMDataLength());
if (returnvalue != NDIOKAY)
return false;
returnvalue = m_DeviceProtocol->PINIT(&portHandle);
if (returnvalue != NDIOKAY)
return false;
returnvalue = m_DeviceProtocol->PENA(&portHandle, ndiTool->GetTrackingPriority()); // Enable tool
if (returnvalue != NDIOKAY)
return false;
return true;
}
else
{
return false;
}
}
void mitk::NDITrackingDevice::SetRotationMode(RotationMode r)
{
m_RotationMode = r;
}
mitk::NDITrackingDevice::~NDITrackingDevice()
{
/* stop tracking and disconnect from tracking device */
if (GetState() == Tracking)
{
this->StopTracking();
}
if (GetState() == Ready)
{
this->CloseConnection();
}
/* cleanup tracking thread */
- if ((m_ThreadID != 0) && (m_MultiThreader.IsNotNull()))
- {
- m_MultiThreader->TerminateThread(m_ThreadID);
- }
- m_MultiThreader = nullptr;
+ if (m_Thread.joinable())
+ m_Thread.join();
+
/* free serial communication interface */
if (m_SerialCommunication.IsNotNull())
{
m_SerialCommunication->ClearReceiveBuffer();
m_SerialCommunication->ClearSendBuffer();
m_SerialCommunication->CloseConnection();
m_SerialCommunication = nullptr;
}
}
void mitk::NDITrackingDevice::SetPortNumber(const PortNumber _arg)
{
if (this->GetState() != Setup)
return;
itkDebugMacro("setting PortNumber to " << _arg);
if (this->m_PortNumber != _arg)
{
this->m_PortNumber = _arg;
this->Modified();
}
}
void mitk::NDITrackingDevice::SetDeviceName(std::string _arg)
{
if (this->GetState() != Setup)
return;
itkDebugMacro("setting eviceName to " << _arg);
if (this->m_DeviceName != _arg)
{
this->m_DeviceName = _arg;
this->Modified();
}
}
void mitk::NDITrackingDevice::SetBaudRate(const BaudRate _arg)
{
if (this->GetState() != Setup)
return;
itkDebugMacro("setting BaudRate to " << _arg);
if (this->m_BaudRate != _arg)
{
this->m_BaudRate = _arg;
this->Modified();
}
}
void mitk::NDITrackingDevice::SetDataBits(const DataBits _arg)
{
if (this->GetState() != Setup)
return;
itkDebugMacro("setting DataBits to " << _arg);
if (this->m_DataBits != _arg)
{
this->m_DataBits = _arg;
this->Modified();
}
}
void mitk::NDITrackingDevice::SetParity(const Parity _arg)
{
if (this->GetState() != Setup)
return;
itkDebugMacro("setting Parity to " << _arg);
if (this->m_Parity != _arg)
{
this->m_Parity = _arg;
this->Modified();
}
}
void mitk::NDITrackingDevice::SetStopBits(const StopBits _arg)
{
if (this->GetState() != Setup)
return;
itkDebugMacro("setting StopBits to " << _arg);
if (this->m_StopBits != _arg)
{
this->m_StopBits = _arg;
this->Modified();
}
}
void mitk::NDITrackingDevice::SetHardwareHandshake(const HardwareHandshake _arg)
{
if (this->GetState() != Setup)
return;
itkDebugMacro("setting HardwareHandshake to " << _arg);
if (this->m_HardwareHandshake != _arg)
{
this->m_HardwareHandshake = _arg;
this->Modified();
}
}
void mitk::NDITrackingDevice::SetIlluminationActivationRate(const IlluminationActivationRate _arg)
{
if (this->GetState() == Tracking)
return;
itkDebugMacro("setting IlluminationActivationRate to " << _arg);
if (this->m_IlluminationActivationRate != _arg)
{
this->m_IlluminationActivationRate = _arg;
this->Modified();
if (this->GetState() == Ready) // if the connection to the tracking system is established, send the new rate to the tracking device too
m_DeviceProtocol->IRATE(this->m_IlluminationActivationRate);
}
}
void mitk::NDITrackingDevice::SetDataTransferMode(const DataTransferMode _arg)
{
itkDebugMacro("setting DataTransferMode to " << _arg);
if (this->m_DataTransferMode != _arg)
{
this->m_DataTransferMode = _arg;
this->Modified();
}
}
mitk::NDIErrorCode mitk::NDITrackingDevice::Send(const std::string* input, bool addCRC)
{
if (input == nullptr)
return SERIALSENDERROR;
std::string message;
if (addCRC == true)
message = *input + CalcCRC(input) + std::string(1, CR);
else
message = *input + std::string(1, CR);
//unsigned int messageLength = message.length() + 1; // +1 for CR
// Clear send buffer
this->ClearSendBuffer();
// Send the date to the device
- MutexLockHolder lock(*m_SerialCommunicationMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_SerialCommunicationMutex);
long returnvalue = m_SerialCommunication->Send(message);
if (returnvalue == 0)
return SERIALSENDERROR;
else
return NDIOKAY;
}
mitk::NDIErrorCode mitk::NDITrackingDevice::Receive(std::string* answer, unsigned int numberOfBytes)
{
if (answer == nullptr)
return SERIALRECEIVEERROR;
- MutexLockHolder lock(*m_SerialCommunicationMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_SerialCommunicationMutex);
long returnvalue = m_SerialCommunication->Receive(*answer, numberOfBytes); // never read more bytes than the device has send, the function will block until enough bytes are send...
if (returnvalue == 0)
return SERIALRECEIVEERROR;
else
return NDIOKAY;
}
mitk::NDIErrorCode mitk::NDITrackingDevice::ReceiveByte(char* answer)
{
if (answer == nullptr)
return SERIALRECEIVEERROR;
std::string m;
- MutexLockHolder lock(*m_SerialCommunicationMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_SerialCommunicationMutex);
long returnvalue = m_SerialCommunication->Receive(m, 1);
if ((returnvalue == 0) || (m.size() != 1))
return SERIALRECEIVEERROR;
*answer = m.at(0);
return NDIOKAY;
}
mitk::NDIErrorCode mitk::NDITrackingDevice::ReceiveLine(std::string* answer)
{
if (answer == nullptr)
return SERIALRECEIVEERROR;
std::string m;
- MutexLockHolder lock(*m_SerialCommunicationMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_SerialCommunicationMutex);
do
{
long returnvalue = m_SerialCommunication->Receive(m, 1);
if ((returnvalue == 0) || (m.size() != 1))
return SERIALRECEIVEERROR;
*answer += m;
} while (m.at(0) != LF);
return NDIOKAY;
}
void mitk::NDITrackingDevice::ClearSendBuffer()
{
- MutexLockHolder lock(*m_SerialCommunicationMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_SerialCommunicationMutex);
m_SerialCommunication->ClearSendBuffer();
}
void mitk::NDITrackingDevice::ClearReceiveBuffer()
{
- MutexLockHolder lock(*m_SerialCommunicationMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_SerialCommunicationMutex);
m_SerialCommunication->ClearReceiveBuffer();
}
const std::string mitk::NDITrackingDevice::CalcCRC(const std::string* input)
{
if (input == nullptr)
return "";
/* the crc16 calculation code is taken from the NDI API guide example code section */
static int oddparity[16] = { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 };
unsigned int data; // copy of the input string's current character
unsigned int crcValue = 0; // the crc value is stored here
unsigned int* puCRC16 = &crcValue; // the algorithm uses a pointer to crcValue, so it's easier to provide that than to change the algorithm
for (unsigned int i = 0; i < input->length(); i++)
{
data = (*input)[i];
data = (data ^ (*(puCRC16)& 0xff)) & 0xff;
*puCRC16 >>= 8;
if (oddparity[data & 0x0f] ^ oddparity[data >> 4])
{
*(puCRC16) ^= 0xc001;
}
data <<= 6;
*puCRC16 ^= data;
data <<= 1;
*puCRC16 ^= data;
}
// crcValue contains now the CRC16 value. Convert it to a string and return it
char returnvalue[13];
sprintf(returnvalue, "%04X", crcValue); // 4 hexadecimal digit with uppercase format
return std::string(returnvalue);
}
bool mitk::NDITrackingDevice::OpenConnection()
{
- //this->m_ModeMutex->Lock();
+ //this->m_ModeMutex.lock();
if (this->GetState() != Setup)
{
mitkThrowException(mitk::IGTException) << "Can only try to open the connection if in setup mode";
}
m_SerialCommunication = mitk::SerialCommunication::New();
/* init local com port to standard com settings for a NDI tracking device:
9600 baud, 8 data bits, no parity, 1 stop bit, no hardware handshake */
if (m_DeviceName.empty())
m_SerialCommunication->SetPortNumber(m_PortNumber);
else
m_SerialCommunication->SetDeviceName(m_DeviceName);
m_SerialCommunication->SetBaudRate(mitk::SerialCommunication::BaudRate9600);
m_SerialCommunication->SetDataBits(mitk::SerialCommunication::DataBits8);
m_SerialCommunication->SetParity(mitk::SerialCommunication::None);
m_SerialCommunication->SetStopBits(mitk::SerialCommunication::StopBits1);
m_SerialCommunication->SetSendTimeout(5000);
m_SerialCommunication->SetReceiveTimeout(5000);
if (m_SerialCommunication->OpenConnection() == 0) // 0 == ERROR_VALUE
{
m_SerialCommunication->CloseConnection();
m_SerialCommunication = nullptr;
mitkThrowException(mitk::IGTHardwareException) << "Can not open serial port";
}
/* Reset Tracking device by sending a serial break for 500ms */
m_SerialCommunication->SendBreak(400);
/* Read answer from tracking device (RESETBE6F) */
static const std::string reset("RESETBE6F\r");
std::string answer = "";
this->Receive(&answer, reset.length()); // read answer (should be RESETBE6F)
this->ClearReceiveBuffer(); // flush the receive buffer of all remaining data (carriage return, strings other than reset
if (reset.compare(answer) != 0) // check for RESETBE6F
{
if (m_SerialCommunication.IsNotNull())
{
m_SerialCommunication->CloseConnection();
m_SerialCommunication = nullptr;
}
mitkThrowException(mitk::IGTHardwareException) << "Hardware Reset of tracking device did not work";
}
/* Now the tracking device isSetData reset, start initialization */
NDIErrorCode returnvalue;
/* set device com settings to new values and wait for the device to change them */
returnvalue = m_DeviceProtocol->COMM(m_BaudRate, m_DataBits, m_Parity, m_StopBits, m_HardwareHandshake);
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << "Could not set comm settings in trackingdevice";
}
//after changing COMM wait at least 100ms according to NDI Api documentation page 31
itksys::SystemTools::Delay(500);
/* now change local com settings accordingly */
m_SerialCommunication->CloseConnection();
m_SerialCommunication->SetBaudRate(m_BaudRate);
m_SerialCommunication->SetDataBits(m_DataBits);
m_SerialCommunication->SetParity(m_Parity);
m_SerialCommunication->SetStopBits(m_StopBits);
m_SerialCommunication->SetHardwareHandshake(m_HardwareHandshake);
m_SerialCommunication->SetSendTimeout(5000);
m_SerialCommunication->SetReceiveTimeout(5000);
m_SerialCommunication->OpenConnection();
/* initialize the tracking device */
returnvalue = m_DeviceProtocol->INIT();
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << "Could not initialize the tracking device";
}
if (this->GetType() == mitk::UnspecifiedTrackingTypeInformation::GetTrackingDeviceName()) // if the type of tracking device is not specified, try to query the connected device
{
mitk::TrackingDeviceType deviceType;
returnvalue = m_DeviceProtocol->VER(deviceType);
if ((returnvalue != NDIOKAY) || (deviceType == mitk::UnspecifiedTrackingTypeInformation::GetTrackingDeviceName()))
{
mitkThrowException(mitk::IGTHardwareException) << "Could not determine tracking device type. Please set manually and try again.";
}
this->SetType(deviceType);
}
/**** Optional Polaris specific code, Work in progress
// start diagnostic mode
returnvalue = m_DeviceProtocol->DSTART();
if (returnvalue != NDIOKAY)
{
this->SetErrorMessage("Could not start diagnostic mode");
return false;
}
else // we are in diagnostic mode
{
// initialize extensive IR checking
returnvalue = m_DeviceProtocol->IRINIT();
if (returnvalue != NDIOKAY)
{
this->SetErrorMessage("Could not initialize intense infrared light checking");
return false;
}
bool intenseIR = false;
returnvalue = m_DeviceProtocol->IRCHK(&intenseIR);
if (returnvalue != NDIOKAY)
{
this->SetErrorMessage("Could not execute intense infrared light checking");
return false;
}
if (intenseIR == true)
// do something - warn the user, raise exception, write to protocol or similar
std::cout << "Warning: Intense infrared light detected. Accurate tracking will probably not be possible.\n";
// stop diagnictic mode
returnvalue = m_DeviceProtocol->DSTOP();
if (returnvalue != NDIOKAY)
{
this->SetErrorMessage("Could not stop diagnostic mode");
return false;
}
}
*** end of optional polaris code ***/
/**
* now add tools to the tracking system
**/
/* First, check if the tracking device has port handles that need to be freed and free them */
returnvalue = FreePortHandles();
// non-critical, therefore no error handling
/**
* POLARIS: initialize the tools that were added manually
**/
{
- MutexLockHolder toolsMutexLockHolder(*m_ToolsMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_ToolsMutex);
std::string portHandle;
auto endIt = m_6DTools.end();
for (auto it = m_6DTools.begin(); it != endIt; ++it)
{
/* get a port handle for the tool */
returnvalue = m_DeviceProtocol->PHRQ(&portHandle);
if (returnvalue == NDIOKAY)
{
(*it)->SetPortHandle(portHandle.c_str());
/* now write the SROM file of the tool to the tracking system using PVWR */
if (this->m_Data.Line == mitk::NDIPolarisTypeInformation::GetTrackingDeviceName())
{
returnvalue = m_DeviceProtocol->PVWR(&portHandle, (*it)->GetSROMData(), (*it)->GetSROMDataLength());
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not write SROM file for tool '") + (*it)->GetToolName() + std::string("' to tracking device")).c_str();
}
returnvalue = m_DeviceProtocol->PINIT(&portHandle);
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not initialize tool '") + (*it)->GetToolName()).c_str();
}
if ((*it)->IsEnabled() == true)
{
returnvalue = m_DeviceProtocol->PENA(&portHandle, (*it)->GetTrackingPriority()); // Enable tool
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not enable port '") + portHandle +
std::string("' for tool '") + (*it)->GetToolName() + std::string("'")).c_str();
}
}
}
}
}
} // end of toolsmutexlockholder scope
/* check for wired tools and add them too */
if (this->DiscoverWiredTools() == false) // query the tracking device for wired tools and add them to our tool list
return false; // \TODO: could we continue anyways?
/*POLARIS: set the illuminator activation rate */
if (this->m_Data.Line == mitk::NDIPolarisTypeInformation::GetTrackingDeviceName())
{
returnvalue = m_DeviceProtocol->IRATE(this->m_IlluminationActivationRate);
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << "Could not set the illuminator activation rate";
}
}
/* finish - now all tools should be added, initialized and enabled, so that tracking can be started */
this->SetState(Ready);
try
{
SetVolume(this->m_Data);
}
catch (const mitk::IGTHardwareException& e)
{
MITK_WARN << e.GetDescription();
}
return true;
}
bool mitk::NDITrackingDevice::InitializeWiredTools()
{
NDIErrorCode returnvalue;
std::string portHandle;
returnvalue = m_DeviceProtocol->PHSR(OCCUPIED, &portHandle);
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << "Could not obtain a list of port handles that are connected";
}
/* if there are port handles that need to be initialized, initialize them. Furthermore instantiate tools for each handle that has no tool yet. */
std::string ph;
for (unsigned int i = 0; i < portHandle.size(); i += 2)
{
ph = portHandle.substr(i, 2);
mitk::NDIPassiveTool* pt = this->GetInternalTool(ph);
if (pt == nullptr) // if we don't have a tool, something is wrong. Tools should be discovered first by calling DiscoverWiredTools()
continue;
if (pt->GetSROMData() == nullptr)
continue;
returnvalue = m_DeviceProtocol->PVWR(&ph, pt->GetSROMData(), pt->GetSROMDataLength());
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not write SROM file for tool '") + pt->GetToolName() + std::string("' to tracking device")).c_str();
}
returnvalue = m_DeviceProtocol->PINIT(&ph);
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not initialize tool '") + pt->GetToolName()).c_str();
}
if (pt->IsEnabled() == true)
{
returnvalue = m_DeviceProtocol->PENA(&ph, pt->GetTrackingPriority()); // Enable tool
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not enable port '") + portHandle +
std::string("' for tool '") + pt->GetToolName() + std::string("'")).c_str();
}
}
}
return true;
}
mitk::TrackingDeviceType mitk::NDITrackingDevice::TestConnection()
{
if (this->GetState() != Setup)
{
return mitk::UnspecifiedTrackingTypeInformation::GetTrackingDeviceName();
}
m_SerialCommunication = mitk::SerialCommunication::New();
//m_DeviceProtocol = mitk::NDIProtocol::New();
//m_DeviceProtocol->SetTrackingDevice(this);
//m_DeviceProtocol->UseCRCOn();
/* init local com port to standard com settings for a NDI tracking device:
9600 baud, 8 data bits, no parity, 1 stop bit, no hardware handshake
*/
if (m_DeviceName.empty())
m_SerialCommunication->SetPortNumber(m_PortNumber);
else
m_SerialCommunication->SetDeviceName(m_DeviceName);
m_SerialCommunication->SetBaudRate(mitk::SerialCommunication::BaudRate9600);
m_SerialCommunication->SetDataBits(mitk::SerialCommunication::DataBits8);
m_SerialCommunication->SetParity(mitk::SerialCommunication::None);
m_SerialCommunication->SetStopBits(mitk::SerialCommunication::StopBits1);
m_SerialCommunication->SetSendTimeout(5000);
m_SerialCommunication->SetReceiveTimeout(5000);
if (m_SerialCommunication->OpenConnection() == 0) // error
{
m_SerialCommunication = nullptr;
return mitk::UnspecifiedTrackingTypeInformation::GetTrackingDeviceName();
}
/* Reset Tracking device by sending a serial break for 500ms */
m_SerialCommunication->SendBreak(400);
/* Read answer from tracking device (RESETBE6F) */
static const std::string reset("RESETBE6F\r");
std::string answer = "";
this->Receive(&answer, reset.length()); // read answer (should be RESETBE6F)
this->ClearReceiveBuffer(); // flush the receive buffer of all remaining data (carriage return, strings other than reset
if (reset.compare(answer) != 0) // check for RESETBE6F
{
m_SerialCommunication->CloseConnection();
m_SerialCommunication = nullptr;
mitkThrowException(mitk::IGTHardwareException) << "Hardware Reset of tracking device did not work";
}
/* Now the tracking device is reset, start initialization */
NDIErrorCode returnvalue;
/* initialize the tracking device */
//returnvalue = m_DeviceProtocol->INIT();
//if (returnvalue != NDIOKAY)
//{
// this->SetErrorMessage("Could not initialize the tracking device");
// return mitk::TrackingSystemNotSpecified;
//}
mitk::TrackingDeviceType deviceType;
returnvalue = m_DeviceProtocol->VER(deviceType);
if ((returnvalue != NDIOKAY) || (deviceType == mitk::UnspecifiedTrackingTypeInformation::GetTrackingDeviceName()))
{
m_SerialCommunication = nullptr;
return mitk::UnspecifiedTrackingTypeInformation::GetTrackingDeviceName();
}
m_SerialCommunication = nullptr;
return deviceType;
}
bool mitk::NDITrackingDevice::CloseConnection()
{
if (this->GetState() != Setup)
{
//init before closing to force the field generator from aurora to switch itself off
m_DeviceProtocol->INIT();
/* close the serial connection */
m_SerialCommunication->CloseConnection();
/* invalidate all tools */
this->InvalidateAll();
/* return to setup mode */
this->SetState(Setup);
m_SerialCommunication = nullptr;
}
return true;
}
-ITK_THREAD_RETURN_TYPE mitk::NDITrackingDevice::ThreadStartTracking(void* pInfoStruct)
+void mitk::NDITrackingDevice::ThreadStartTracking()
{
- /* extract this pointer from Thread Info structure */
- struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct;
- if (pInfo == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
- if (pInfo->UserData == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
- NDITrackingDevice *trackingDevice = (NDITrackingDevice*)pInfo->UserData;
- if (trackingDevice != nullptr)
+ if (this->GetOperationMode() == ToolTracking6D)
+ this->TrackTools(); // call TrackTools() from the original object
+ else if (this->GetOperationMode() == MarkerTracking3D)
+ this->TrackMarkerPositions(); // call TrackMarkerPositions() from the original object
+ else if (this->GetOperationMode() == ToolTracking5D)
+ this->TrackMarkerPositions(); // call TrackMarkerPositions() from the original object
+ else if (this->GetOperationMode() == HybridTracking)
{
- if (trackingDevice->GetOperationMode() == ToolTracking6D)
- trackingDevice->TrackTools(); // call TrackTools() from the original object
- else if (trackingDevice->GetOperationMode() == MarkerTracking3D)
- trackingDevice->TrackMarkerPositions(); // call TrackMarkerPositions() from the original object
- else if (trackingDevice->GetOperationMode() == ToolTracking5D)
- trackingDevice->TrackMarkerPositions(); // call TrackMarkerPositions() from the original object
- else if (trackingDevice->GetOperationMode() == HybridTracking)
- {
- trackingDevice->TrackToolsAndMarkers();
- }
+ this->TrackToolsAndMarkers();
}
- trackingDevice->m_ThreadID = 0; // erase thread id, now that this thread will end.
- return ITK_THREAD_RETURN_VALUE;
}
bool mitk::NDITrackingDevice::StartTracking()
{
if (this->GetState() != Ready)
return false;
this->SetState(Tracking); // go to mode Tracking
- this->m_StopTrackingMutex->Lock(); // update the local copy of m_StopTracking
+ this->m_StopTrackingMutex.lock(); // update the local copy of m_StopTracking
this->m_StopTracking = false;
- this->m_StopTrackingMutex->Unlock();
+ this->m_StopTrackingMutex.unlock();
- m_ThreadID = m_MultiThreader->SpawnThread(this->ThreadStartTracking, this); // start a new thread that executes the TrackTools() method
+ m_Thread = std::thread(&NDITrackingDevice::ThreadStartTracking, this); // start a new thread that executes the TrackTools() method
mitk::IGTTimeStamp::GetInstance()->Start(this);
return true;
}
void mitk::NDITrackingDevice::TrackTools()
{
/* lock the TrackingFinishedMutex to signal that the execution rights are now transfered to the tracking thread */
- MutexLockHolder trackingFinishedLockHolder(*m_TrackingFinishedMutex); // keep lock until end of scope
+ std::lock_guard<std::mutex> lock(m_TrackingFinishedMutex);
if (this->GetState() != Tracking)
return;
NDIErrorCode returnvalue;
returnvalue = m_DeviceProtocol->TSTART();
if (returnvalue != NDIOKAY)
return;
bool localStopTracking; // Because m_StopTracking is used by two threads, access has to be guarded by a mutex. To minimize thread locking, a local copy is used here
- this->m_StopTrackingMutex->Lock(); // update the local copy of m_StopTracking
+ this->m_StopTrackingMutex.lock(); // update the local copy of m_StopTracking
localStopTracking = this->m_StopTracking;
- this->m_StopTrackingMutex->Unlock();
+ this->m_StopTrackingMutex.unlock();
while ((this->GetState() == Tracking) && (localStopTracking == false))
{
if (this->m_DataTransferMode == TX)
{
returnvalue = this->m_DeviceProtocol->TX();
if (!((returnvalue == NDIOKAY) || (returnvalue == NDICRCERROR) || (returnvalue == NDICRCDOESNOTMATCH))) // right now, do not stop on crc errors
break;
}
else
{
returnvalue = this->m_DeviceProtocol->BX();
if (returnvalue != NDIOKAY)
break;
}
/* Update the local copy of m_StopTracking */
- this->m_StopTrackingMutex->Lock();
+ this->m_StopTrackingMutex.lock();
localStopTracking = m_StopTracking;
- this->m_StopTrackingMutex->Unlock();
+ this->m_StopTrackingMutex.unlock();
}
/* StopTracking was called, thus the mode should be changed back to Ready now that the tracking loop has ended. */
returnvalue = m_DeviceProtocol->TSTOP();
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << "An error occured while tracking tools.";
}
return; // returning from this function (and ThreadStartTracking()) this will end the thread and transfer control back to main thread by releasing trackingFinishedLockHolder
}
void mitk::NDITrackingDevice::TrackMarkerPositions()
{
- MutexLockHolder trackingFinishedLockHolder(*m_TrackingFinishedMutex); // keep lock until end of scope
+ std::lock_guard<std::mutex> lock(m_TrackingFinishedMutex);
if (m_OperationMode == ToolTracking6D)
return;
if (this->GetState() != Tracking)
return;
NDIErrorCode returnvalue;
returnvalue = m_DeviceProtocol->DSTART(); // Start Diagnostic Mode
if (returnvalue != NDIOKAY)
return;
bool localStopTracking; // Because m_StopTracking is used by two threads, access has to be guarded by a mutex. To minimize thread locking, a local copy is used here
- this->m_StopTrackingMutex->Lock(); // update the local copy of m_StopTracking
+ this->m_StopTrackingMutex.lock(); // update the local copy of m_StopTracking
localStopTracking = this->m_StopTracking;
- this->m_StopTrackingMutex->Unlock();
+ this->m_StopTrackingMutex.unlock();
while ((this->GetState() == Tracking) && (localStopTracking == false))
{
- m_MarkerPointsMutex->Lock(); // lock points data structure
+ m_MarkerPointsMutex.lock(); // lock points data structure
returnvalue = this->m_DeviceProtocol->POS3D(&m_MarkerPoints); // update points data structure with new position data from tracking device
- m_MarkerPointsMutex->Unlock();
+ m_MarkerPointsMutex.unlock();
if (!((returnvalue == NDIOKAY) || (returnvalue == NDICRCERROR) || (returnvalue == NDICRCDOESNOTMATCH))) // right now, do not stop on crc errors
{
std::cout << "Error in POS3D: could not read data. Possibly no markers present." << std::endl;
}
/* Update the local copy of m_StopTracking */
- this->m_StopTrackingMutex->Lock();
+ this->m_StopTrackingMutex.lock();
localStopTracking = m_StopTracking;
- this->m_StopTrackingMutex->Unlock();
+ this->m_StopTrackingMutex.unlock();
itksys::SystemTools::Delay(1);
}
/* StopTracking was called, thus the mode should be changed back to Ready now that the tracking loop has ended. */
returnvalue = m_DeviceProtocol->DSTOP();
if (returnvalue != NDIOKAY)
return; // how can this thread tell the application, that an error has occured?
this->SetState(Ready);
return; // returning from this function (and ThreadStartTracking()) this will end the thread
}
void mitk::NDITrackingDevice::TrackToolsAndMarkers()
{
- MutexLockHolder trackingFinishedLockHolder(*m_TrackingFinishedMutex); // keep lock until end of scope
+ std::lock_guard<std::mutex> lock(m_TrackingFinishedMutex);
if (m_OperationMode != HybridTracking)
return;
NDIErrorCode returnvalue;
returnvalue = m_DeviceProtocol->TSTART(); // Start Diagnostic Mode
if (returnvalue != NDIOKAY)
return;
bool localStopTracking; // Because m_StopTracking is used by two threads, access has to be guarded by a mutex. To minimize thread locking, a local copy is used here
- this->m_StopTrackingMutex->Lock(); // update the local copy of m_StopTracking
+ this->m_StopTrackingMutex.lock(); // update the local copy of m_StopTracking
localStopTracking = this->m_StopTracking;
- this->m_StopTrackingMutex->Unlock();
+ this->m_StopTrackingMutex.unlock();
while ((this->GetState() == Tracking) && (localStopTracking == false))
{
- m_MarkerPointsMutex->Lock(); // lock points data structure
+ m_MarkerPointsMutex.lock(); // lock points data structure
returnvalue = this->m_DeviceProtocol->TX(true, &m_MarkerPoints); // update points data structure with new position data from tracking device
- m_MarkerPointsMutex->Unlock();
+ m_MarkerPointsMutex.unlock();
if (!((returnvalue == NDIOKAY) || (returnvalue == NDICRCERROR) || (returnvalue == NDICRCDOESNOTMATCH))) // right now, do not stop on crc errors
{
std::cout << "Error in TX: could not read data. Possibly no markers present." << std::endl;
}
/* Update the local copy of m_StopTracking */
- this->m_StopTrackingMutex->Lock();
+ this->m_StopTrackingMutex.lock();
localStopTracking = m_StopTracking;
- this->m_StopTrackingMutex->Unlock();
+ this->m_StopTrackingMutex.unlock();
}
/* StopTracking was called, thus the mode should be changed back to Ready now that the tracking loop has ended. */
returnvalue = m_DeviceProtocol->TSTOP();
if (returnvalue != NDIOKAY)
return; // how can this thread tell the application, that an error has occurred?
this->SetState(Ready);
return; // returning from this function (and ThreadStartTracking()) this will end the thread
}
mitk::TrackingTool* mitk::NDITrackingDevice::GetTool(unsigned int toolNumber) const
{
- MutexLockHolder toolsMutexLockHolder(*m_ToolsMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_ToolsMutex);
if (toolNumber < m_6DTools.size())
return m_6DTools.at(toolNumber);
return nullptr;
}
mitk::TrackingTool* mitk::NDITrackingDevice::GetToolByName(std::string name) const
{
- MutexLockHolder toolsMutexLockHolder(*m_ToolsMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_ToolsMutex);
auto end = m_6DTools.end();
for (auto iterator = m_6DTools.begin(); iterator != end; ++iterator)
if (name.compare((*iterator)->GetToolName()) == 0)
return *iterator;
return nullptr;
}
mitk::NDIPassiveTool* mitk::NDITrackingDevice::GetInternalTool(std::string portHandle)
{
- MutexLockHolder toolsMutexLockHolder(*m_ToolsMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_ToolsMutex);
auto end = m_6DTools.end();
for (auto iterator = m_6DTools.begin(); iterator != end; ++iterator)
if (portHandle.compare((*iterator)->GetPortHandle()) == 0)
return *iterator;
return nullptr;
}
unsigned int mitk::NDITrackingDevice::GetToolCount() const
{
- MutexLockHolder toolsMutexLockHolder(*m_ToolsMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_ToolsMutex);
return m_6DTools.size();
}
bool mitk::NDITrackingDevice::Beep(unsigned char count)
{
if (this->GetState() != Setup)
{
return (m_DeviceProtocol->BEEP(count) == NDIOKAY);
}
else
{
return false;
}
}
mitk::TrackingTool* mitk::NDITrackingDevice::AddTool(const char* toolName, const char* fileName, TrackingPriority p /*= NDIPassiveTool::Dynamic*/)
{
mitk::NDIPassiveTool::Pointer t = mitk::NDIPassiveTool::New();
if (t->LoadSROMFile(fileName) == false)
return nullptr;
t->SetToolName(toolName);
t->SetTrackingPriority(p);
if (this->InternalAddTool(t) == false)
return nullptr;
return t.GetPointer();
}
bool mitk::NDITrackingDevice::InternalAddTool(mitk::NDIPassiveTool* tool)
{
if (tool == nullptr)
return false;
NDIPassiveTool::Pointer p = tool;
/* if the connection to the tracking device is already established, add the new tool to the device now */
if (this->GetState() == Ready)
{
/* get a port handle for the tool */
std::string newPortHandle;
NDIErrorCode returnvalue;
returnvalue = m_DeviceProtocol->PHRQ(&newPortHandle);
if (returnvalue == NDIOKAY)
{
p->SetPortHandle(newPortHandle.c_str());
/* now write the SROM file of the tool to the tracking system using PVWR */
returnvalue = m_DeviceProtocol->PVWR(&newPortHandle, p->GetSROMData(), p->GetSROMDataLength());
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not write SROM file for tool '") + p->GetToolName() + std::string("' to tracking device")).c_str();
}
/* initialize the port handle */
returnvalue = m_DeviceProtocol->PINIT(&newPortHandle);
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not initialize port '") + newPortHandle +
std::string("' for tool '") + p->GetToolName() + std::string("'")).c_str();
}
/* enable the port handle */
if (p->IsEnabled() == true)
{
returnvalue = m_DeviceProtocol->PENA(&newPortHandle, p->GetTrackingPriority()); // Enable tool
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not enable port '") + newPortHandle +
std::string("' for tool '") + p->GetToolName() + std::string("'")).c_str();
}
}
}
/* now that the tool is added to the device, add it to list too */
- m_ToolsMutex->Lock();
+ m_ToolsMutex.lock();
this->m_6DTools.push_back(p);
- m_ToolsMutex->Unlock();
+ m_ToolsMutex.unlock();
this->Modified();
return true;
}
else if (this->GetState() == Setup)
{
/* In Setup mode, we only add it to the list, so that OpenConnection() can add it later */
- m_ToolsMutex->Lock();
+ m_ToolsMutex.lock();
this->m_6DTools.push_back(p);
- m_ToolsMutex->Unlock();
+ m_ToolsMutex.unlock();
this->Modified();
return true;
}
else // in Tracking mode, no tools can be added
return false;
}
bool mitk::NDITrackingDevice::RemoveTool(mitk::TrackingTool* tool)
{
mitk::NDIPassiveTool* ndiTool = dynamic_cast<mitk::NDIPassiveTool*>(tool);
if (ndiTool == nullptr)
return false;
std::string portHandle = ndiTool->GetPortHandle();
/* a valid portHandle has length 2. If a valid handle exists, the tool is already added to the tracking device, so we have to remove it there
if the connection to the tracking device has already been established.
*/
if ((portHandle.length() == 2) && (this->GetState() == Ready)) // do not remove a tool in tracking mode
{
NDIErrorCode returnvalue;
returnvalue = m_DeviceProtocol->PHF(&portHandle);
if (returnvalue != NDIOKAY)
return false;
/* Now that the tool is removed from the tracking device, remove it from our tool list too */
- MutexLockHolder toolsMutexLockHolder(*m_ToolsMutex); // lock and unlock the mutex (scope is inside the if-block
+ std::lock_guard<std::mutex> lock(m_ToolsMutex);
auto end = m_6DTools.end();
for (auto iterator = m_6DTools.begin(); iterator != end; ++iterator)
{
if (iterator->GetPointer() == ndiTool)
{
m_6DTools.erase(iterator);
this->Modified();
return true;
}
}
return false;
}
else if (this->GetState() == Setup) // in Setup Mode, we are not connected to the tracking device, so we can just remove the tool from the tool list
{
- MutexLockHolder toolsMutexLockHolder(*m_ToolsMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_ToolsMutex);
auto end = m_6DTools.end();
for (auto iterator = m_6DTools.begin(); iterator != end; ++iterator)
{
if ((*iterator).GetPointer() == ndiTool)
{
m_6DTools.erase(iterator);
this->Modified();
return true;
}
}
return false;
}
return false;
}
void mitk::NDITrackingDevice::InvalidateAll()
{
- MutexLockHolder toolsMutexLockHolder(*m_ToolsMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_ToolsMutex);
auto end = m_6DTools.end();
for (auto iterator = m_6DTools.begin(); iterator != end; ++iterator)
(*iterator)->SetDataValid(false);
}
bool mitk::NDITrackingDevice::SetOperationMode(OperationMode mode)
{
if (GetState() == Tracking)
return false;
m_OperationMode = mode;
return true;
}
mitk::OperationMode mitk::NDITrackingDevice::GetOperationMode()
{
return m_OperationMode;
}
bool mitk::NDITrackingDevice::GetMarkerPositions(MarkerPointContainerType* markerpositions)
{
- m_MarkerPointsMutex->Lock();
+ m_MarkerPointsMutex.lock();
*markerpositions = m_MarkerPoints; // copy the internal vector to the one provided
- m_MarkerPointsMutex->Unlock();
+ m_MarkerPointsMutex.unlock();
return (markerpositions->size() != 0);
}
bool mitk::NDITrackingDevice::DiscoverWiredTools()
{
/* First, check for disconnected tools and remove them */
this->FreePortHandles();
//NDI handling (PHSR 02, PINIT, PHSR 02, PHSR 00) => all initialized and all handles available
//creation of MITK tools
//NDI enable all tools (PENA)
//NDI get all serial numbers (PHINF)
/**
NDI handling (PHSR 02, PINIT, PHSR 02, PHSR 00) => all initialized and all handles available
**/
/* check for occupied port handles on channel 0 */
std::string portHandle;
NDIErrorCode returnvalue = m_DeviceProtocol->PHSR(OCCUPIED, &portHandle);
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << "Could not obtain a list of port handles that are connected on channel 0.";
}
/* Initialize all port handles on channel 0 */
for (unsigned int i = 0; i < portHandle.size(); i += 2)
{
std::string ph = portHandle.substr(i, 2);
returnvalue = m_DeviceProtocol->PINIT(&ph);
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not initialize port '") + ph + std::string("."));
}
}
/* check for occupied port handles on channel 1 (initialize automatically, portHandle is empty although additional tools were detected) */
//For a split port on a dual 5DOF tool, the first PHSR sent will report only one port handle. After the port handle is
//initialized, it is assigned to channel 0. You must then use PHSR again to assign a port handle to channel 1. The
//port handle for channel 1 is initialized automatically.
returnvalue = m_DeviceProtocol->PHSR(OCCUPIED, &portHandle);
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << "Could not obtain a list of port handles that are connected on channel 1.";
}
/* read all port handles */
returnvalue = m_DeviceProtocol->PHSR(ALL, &portHandle);
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << "Could not obtain a list of port handles that are connected on all channels.";
}
/**
1. Create MITK tracking tool representations of NDI tools
2. NDI enable all tools (PENA)
**/
for (unsigned int i = 0; i < portHandle.size(); i += 2)
{
std::string ph = portHandle.substr(i, 2);
if (this->GetInternalTool(ph) != nullptr) // if we already have a tool with this handle
continue; // then skip the initialization
//define tracking priority
auto trackingPriority = mitk::NDIPassiveTool::Dynamic;
//instantiate an object for each tool that is connected
mitk::NDIPassiveTool::Pointer newTool = mitk::NDIPassiveTool::New();
newTool->SetPortHandle(ph.c_str());
newTool->SetTrackingPriority(trackingPriority);
//set a name for identification
newTool->SetToolName((std::string("Port ") + ph).c_str());
/* enable the port handle */
returnvalue = m_DeviceProtocol->PENA(&ph, trackingPriority); // Enable tool
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not enable port '") + ph +
std::string("' for tool '") + newTool->GetToolName() + std::string("'")).c_str();
}
//we have to temporarily unlock m_ModeMutex here to avoid a deadlock with another lock inside InternalAddTool()
if (this->InternalAddTool(newTool) == false)
{
mitkThrowException(mitk::IGTException) << "Error while adding new tool";
}
}
/**
NDI get all serial numbers (PHINF)
**/
// after initialization readout serial numbers of automatically detected tools
for (unsigned int i = 0; i < portHandle.size(); i += 2)
{
std::string ph = portHandle.substr(i, 2);
std::string portInfo;
NDIErrorCode returnvaluePort = m_DeviceProtocol->PHINF(ph, &portInfo);
if ((returnvaluePort == NDIOKAY) && (portInfo.size() > 31))
dynamic_cast<mitk::NDIPassiveTool*>(this->GetInternalTool(ph))->SetSerialNumber(portInfo.substr(23, 8));
MITK_INFO << "portInfo: " << portInfo;
itksys::SystemTools::Delay(10);
}
return true;
}
mitk::NDIErrorCode mitk::NDITrackingDevice::FreePortHandles()
{
/* first search for port handles that need to be freed: e.g. because of a reset of the tracking system */
NDIErrorCode returnvalue = NDIOKAY;
std::string portHandle;
returnvalue = m_DeviceProtocol->PHSR(FREED, &portHandle);
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << "Could not obtain a list of port handles that need to be freed";
}
/* if there are port handles that need to be freed, free them */
if (portHandle.empty() == true)
return returnvalue;
std::string ph;
for (unsigned int i = 0; i < portHandle.size(); i += 2)
{
ph = portHandle.substr(i, 2);
mitk::NDIPassiveTool* t = this->GetInternalTool(ph);
if (t != nullptr) // if we have a tool for the port handle that needs to be freed
{
if (this->RemoveTool(t) == false) // remove it (this will free the port too)
returnvalue = NDIERROR;
}
else // we don't have a tool, the port handle exists only in the tracking device
{
returnvalue = m_DeviceProtocol->PHF(&ph); // free it there
// What to do if port handle could not be freed? This seems to be a non critical error
if (returnvalue != NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << "Could not free all port handles";
}
}
}
return returnvalue;
}
int mitk::NDITrackingDevice::GetMajorFirmwareRevisionNumber()
{
std::string revision;
if (m_DeviceProtocol->APIREV(&revision) != mitk::NDIOKAY || revision.empty() || (revision.size() != 9))
{
MITK_ERROR << "Could not receive firmware revision number!";
return 0;
}
const std::string majrevno = revision.substr(2, 3); //cut out "004" from "D.004.001"
return std::atoi(majrevno.c_str());
}
const char* mitk::NDITrackingDevice::GetFirmwareRevisionNumber()
{
static std::string revision;
if (m_DeviceProtocol->APIREV(&revision) != mitk::NDIOKAY || revision.empty() || (revision.size() != 9))
{
MITK_ERROR << "Could not receive firmware revision number!";
revision = "";
return revision.c_str();
}
return revision.c_str();
}
bool mitk::NDITrackingDevice::AutoDetectToolsAvailable()
{
if (this->GetType() == mitk::NDIAuroraTypeInformation::GetTrackingDeviceName()) { return true; }
else { return false; }
}
bool mitk::NDITrackingDevice::AddSingleToolIsAvailable()
{
//For Aurora, only AutoDetecion or loading of toolStorage should be used. It is not possible to add a single tool.
if (this->GetType() == mitk::NDIAuroraTypeInformation::GetTrackingDeviceName()) { return false; }
//For Polaris, a single tool can be added, there is no autoDetection.
else { return true; }
}
mitk::NavigationToolStorage::Pointer mitk::NDITrackingDevice::AutoDetectTools()
{
mitk::NavigationToolStorage::Pointer autoDetectedStorage = mitk::NavigationToolStorage::New();
if (this->GetType() == mitk::NDIAuroraTypeInformation::GetTrackingDeviceName())
{
try
{
this->OpenConnection();
this->StartTracking();
}
catch (mitk::Exception& e)
{
MITK_WARN << "Warning, can not auto-detect tools! (" << e.GetDescription() << ")";
return autoDetectedStorage;
}
for (unsigned int i = 0; i < this->GetToolCount(); i++)
{
//create a navigation tool with sphere as surface
std::stringstream toolname;
toolname << "AutoDetectedTool" << i;
mitk::NavigationTool::Pointer newTool = mitk::NavigationTool::New();
newTool->SetSerialNumber(dynamic_cast<mitk::NDIPassiveTool*>(this->GetTool(i))->GetSerialNumber());
newTool->SetIdentifier(toolname.str());
newTool->SetTrackingDeviceType(mitk::NDIAuroraTypeInformation::GetTrackingDeviceName());
newTool->GetDataNode()->SetName(toolname.str());
autoDetectedStorage->AddTool(newTool);
}
this->StopTracking();
this->CloseConnection();
}
return autoDetectedStorage;
}
bool mitk::NDITrackingDevice::GetSupportedVolumes(unsigned int* numberOfVolumes, mitk::NDITrackingDevice::NDITrackingVolumeContainerType* volumes, mitk::NDITrackingDevice::TrackingVolumeDimensionType* volumesDimensions)
{
if (numberOfVolumes == nullptr || volumes == nullptr || volumesDimensions == nullptr)
return false;
static std::string info;
if (m_DeviceProtocol->SFLIST(&info) != mitk::NDIOKAY || info.empty())
{
MITK_ERROR << "Could not receive tracking volume information of tracking system!";
return false;
}
/*info contains the following:
<HEX:number of volumes> (+n times:) <HEX:shape type> <shape parameters D1-D10> <HEX:reserved / number of wavelength supported> <metal resistant / supported wavelength>
*/
(*numberOfVolumes) = (unsigned int)std::atoi(info.substr(0, 1).c_str());
for (unsigned int i = 0; i < (*numberOfVolumes); i++)
{
//e.g. for cube: "9-025000+025000-025000+025000-055000-005000+000000+000000+000000+00000011"
//for dome: "A+005000+048000+005000+066000+000000+000000+000000+000000+000000+00000011"
std::string::size_type offset, end;
offset = (i * 73) + 1;
end = 73 + (i * 73);
std::string currentVolume = info.substr(offset, end);//i=0: from 1 to 73 characters; i=1: from 75 to 148 char;
// if i>0 then we have a return statement <LF> infront
if (i > 0)
currentVolume = currentVolume.substr(1, currentVolume.size());
if (currentVolume.compare(0, 1, NDIPolarisTypeInformation::GetDeviceDataPolarisOldModel().HardwareCode) == 0)
volumes->push_back(NDIPolarisTypeInformation::GetDeviceDataPolarisOldModel().Model);
if (currentVolume.compare(0, 3, NDIPolarisTypeInformation::GetDeviceDataPolarisSpectra().HardwareCode) == 0)
volumes->push_back(NDIPolarisTypeInformation::GetDeviceDataPolarisSpectra().Model);
if (currentVolume.compare(1, 3, NDIPolarisTypeInformation::GetDeviceDataSpectraExtendedPyramid().HardwareCode) == 0)
{
currentVolume = currentVolume.substr(1, currentVolume.size());
volumes->push_back(NDIPolarisTypeInformation::GetDeviceDataSpectraExtendedPyramid().Model);
}
if (currentVolume.compare(0, 1, NDIPolarisTypeInformation::GetDeviceDataPolarisVicra().HardwareCode) == 0)
volumes->push_back(NDIPolarisTypeInformation::GetDeviceDataPolarisVicra().Model);
else if (currentVolume.compare(0, 1, mitk::NDIAuroraTypeInformation::GetDeviceDataAuroraPlanarCube().HardwareCode) == 0)
volumes->push_back(mitk::NDIAuroraTypeInformation::GetDeviceDataAuroraPlanarCube().Model);//alias cube
else if (currentVolume.compare(0, 1, mitk::NDIAuroraTypeInformation::GetDeviceDataAuroraPlanarDome().HardwareCode) == 0)
volumes->push_back(mitk::NDIAuroraTypeInformation::GetDeviceDataAuroraPlanarDome().Model);
//fill volumesDimensions
for (unsigned int index = 0; index < 10; index++)
{
std::string::size_type offD, endD;
offD = 1 + (index * 7); //7 digits per dimension and the first is the type of volume
endD = offD + 7;
int dimension = std::atoi(currentVolume.substr(offD, endD).c_str());
dimension /= 100; //given in mm. 7 digits are xxxx.xx according to NDI //strange, the last two digits (11) also for the metal flag get read also...
volumesDimensions->push_back(dimension);
}
}
return true;
}
bool mitk::NDITrackingDevice::SetVolume(mitk::TrackingDeviceData volume)
{
if (m_DeviceProtocol->VSEL(volume) != mitk::NDIOKAY)
{
mitkThrowException(mitk::IGTHardwareException) << "Could not set volume!";
}
return true;
}
diff --git a/Modules/IGT/TrackingDevices/mitkNDITrackingDevice.h b/Modules/IGT/TrackingDevices/mitkNDITrackingDevice.h
index 7a160e34e1..e38481e59e 100644
--- a/Modules/IGT/TrackingDevices/mitkNDITrackingDevice.h
+++ b/Modules/IGT/TrackingDevices/mitkNDITrackingDevice.h
@@ -1,330 +1,329 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKNDITRACKINGDEVICE_H_HEADER_INCLUDED_C1C2FCD2
#define MITKNDITRACKINGDEVICE_H_HEADER_INCLUDED_C1C2FCD2
#include "mitkTrackingDevice.h"
#include <MitkIGTExports.h>
-#include <itkMultiThreader.h>
-#include "itkFastMutexLock.h"
+#include <thread>
+#include <mutex>
#include <vector>
#include "mitkNDIProtocol.h"
#include "mitkNDIPassiveTool.h"
#include "mitkSerialCommunication.h"
namespace mitk
{
class NDIProtocol;
/** Documentation
* \brief superclass for specific NDI tracking Devices that use serial communication.
*
* implements the TrackingDevice interface for NDI tracking devices (POLARIS, AURORA)
*
* \ingroup IGT
*/
class MITKIGT_EXPORT NDITrackingDevice : public TrackingDevice
{
friend class NDIProtocol;
public:
typedef std::vector<NDIPassiveTool::Pointer> Tool6DContainerType; ///< List of 6D tools of the correct type for this tracking device
typedef mitk::TrackingDeviceType NDITrackingDeviceType; ///< This enumeration includes the two types of NDI tracking devices (Polaris, Aurora).
typedef mitk::SerialCommunication::PortNumber PortNumber; ///< Port number of the serial connection
typedef mitk::SerialCommunication::BaudRate BaudRate; ///< Baud rate of the serial connection
typedef mitk::SerialCommunication::DataBits DataBits; ///< Number of data bits used in the serial connection
typedef mitk::SerialCommunication::Parity Parity; ///< Parity mode used in the serial connection
typedef mitk::SerialCommunication::StopBits StopBits; ///< Number of stop bits used in the serial connection
typedef mitk::SerialCommunication::HardwareHandshake HardwareHandshake; ///< Hardware handshake mode of the serial connection
typedef mitk::NDIPassiveTool::TrackingPriority TrackingPriority; ///< Tracking priority used for tracking a tool
mitkClassMacro(NDITrackingDevice, TrackingDevice);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* \brief Set the type of the NDI Tracking Device because it can not jet handle this itself
*/
//itkSetMacro(Type, TrackingDeviceType);
/**
* \brief initialize the connection to the tracking device
*
* OpenConnection() establishes the connection to the tracking device by:
* - initializing the serial port with the given parameters (port number, baud rate, ...)
* - connection to the tracking device
* - initializing the device
* - initializing all manually added passive tools (user supplied srom file)
* - initializing active tools that are connected to the tracking device
* @throw mitk::IGTHardwareException Throws an exception if there are errors while connecting to the device.
* @throw mitk::IGTException Throws a normal IGT exception if an error occures which is not related to the hardware.
*/
bool OpenConnection() override;
/**
* \brief Closes the connection
*
* CloseConnection() resets the tracking device, invalidates all tools and then closes the serial port.
*/
bool CloseConnection() override;
/** @throw mitk::IGTHardwareException Throws an exception if there are errors while connecting to the device. */
bool InitializeWiredTools();
/** Sets the rotation mode of this class. See documentation of enum RotationMode for details
* on the different modes.
*/
void SetRotationMode(RotationMode r) override;
/**
* \brief TestConnection() tries to connect to a NDI tracking device on the current port/device and returns which device it has found
*
* TestConnection() tries to connect to a NDI tracking device on the current port/device.
* \return It returns the type of the device that answers at the port/device. Throws an exception if no device is available on that port.
* @throw mitk::IGTHardwareException Throws an exception if there are errors while connecting to the device.
*/
virtual mitk::TrackingDeviceType TestConnection();
/**
* \brief retrieves all wired tools from the tracking device
*
* This method queries the tracking device for all wired tools, initializes them and creates TrackingTool representation objects
* for them
* \return True if the method was executed successful.
* @throw mitk::IGTHardwareException Throws an exception if there are errors while connecting to the device.
* @throw mitk::IGTException Throws a normal IGT exception if an error occures which is not related to the hardware.
*/
bool DiscoverWiredTools();
/**
* \brief Start the tracking.
*
* A new thread is created, which continuously reads the position and orientation information of each tool and stores them inside the tools.
* Depending on the current operation mode (see SetOperationMode()), either the 6D tools (ToolTracking6D), 5D tools (ToolTracking5D),
* 3D marker positions (MarkerTracking3D) or both 6D tools and 3D markers (HybridTracking) are updated.
* Call StopTracking() to stop the tracking thread.
*/
bool StartTracking() override;
/**
* \brief return the tool with index toolNumber
*/
TrackingTool* GetTool(unsigned int toolNumber) const override;
mitk::TrackingTool* GetToolByName(std::string name) const override;
/**
* \brief return current number of tools
*/
unsigned int GetToolCount() const override;
/**
* \brief Create a passive 6D tool with toolName and fileName and add it to the list of tools
*
* This method will create a new NDIPassiveTool object, load the SROM file fileName,
* set the tool name toolName and the tracking priority p and then add
* it to the list of tools. It returns a pointer of type mitk::TrackingTool to the tool
* that can be used to read tracking data from it.
* This is the only way to add tools to NDITrackingDevice.
* @throw mitk::IGTHardwareException Throws an exception if there are errors while adding the tool.
*
* \warning adding tools is not possible in tracking mode, only in setup and ready.
*/
mitk::TrackingTool* AddTool(const char* toolName, const char* fileName, TrackingPriority p = NDIPassiveTool::Dynamic);
/**
* \brief Remove a passive 6D tool from the list of tracked tools.
*
* \warning removing tools is not possible in tracking mode, only in setup and ready modes.
*/
virtual bool RemoveTool(TrackingTool* tool);
/**
* \brief reloads the srom file and reinitializes the tool
*/
virtual bool UpdateTool(mitk::TrackingTool* tool);
virtual void SetPortNumber(const PortNumber _arg); ///< set port number for serial communication
itkGetConstMacro(PortNumber, PortNumber); ///< returns the port number for serial communication
virtual void SetDeviceName(std::string _arg); ///< set device name (e.g. COM1, /dev/ttyUSB0). If this is set, PortNumber will be ignored
itkGetStringMacro(DeviceName); ///< returns the device name for serial communication
virtual void SetBaudRate(const BaudRate _arg); ///< set baud rate for serial communication
itkGetConstMacro(BaudRate, BaudRate); ///< returns the baud rate for serial communication
virtual void SetDataBits(const DataBits _arg); ///< set number of data bits
itkGetConstMacro(DataBits, DataBits); ///< returns the data bits for serial communication
virtual void SetParity(const Parity _arg); ///< set parity mode
itkGetConstMacro(Parity, Parity); ///< returns the parity mode
virtual void SetStopBits(const StopBits _arg); ///< set number of stop bits
itkGetConstMacro(StopBits, StopBits); ///< returns the number of stop bits
virtual void SetHardwareHandshake(const HardwareHandshake _arg); ///< set use hardware handshake for serial communication
itkGetConstMacro(HardwareHandshake, HardwareHandshake); ///< returns the hardware handshake setting
virtual void SetIlluminationActivationRate(const IlluminationActivationRate _arg); ///< set activation rate of IR illumator for polaris
itkGetConstMacro(IlluminationActivationRate, IlluminationActivationRate); ///< returns the activation rate of IR illumator for polaris
virtual void SetDataTransferMode(const DataTransferMode _arg); ///< set data transfer mode to text (TX) or binary (BX). \warning: only TX is supportet at the moment
itkGetConstMacro(DataTransferMode, DataTransferMode); ///< returns the data transfer mode
virtual bool Beep(unsigned char count); ///< Beep the tracking device 1 to 9 times
NDIErrorCode GetErrorCode(const std::string* input); ///< returns the error code for a string that contains an error code in hexadecimal format
virtual bool SetOperationMode(OperationMode mode); ///< set operation mode to 6D tool tracking, 3D marker tracking or 6D&3D hybrid tracking (see OperationMode)
virtual OperationMode GetOperationMode(); ///< get current operation mode
/**
* \brief Get 3D marker positions (operation mode must be set to MarkerTracking3D or HybridTracking)
*/
virtual bool GetMarkerPositions(MarkerPointContainerType* markerpositions);
/**
* \brief Get major revision number from tracking device
* should not be called directly after starting to track
**/
virtual int GetMajorFirmwareRevisionNumber();
/**
* \brief Get revision number from tracking device as string
* should not be called directly after starting to track
**/
virtual const char* GetFirmwareRevisionNumber();
/** @return Returns true if this device can autodetects its tools. */
bool AutoDetectToolsAvailable() override;
/** @return Returns true if it is possible to add a single tool. True for Polaris, false for Aurora.*/
bool AddSingleToolIsAvailable() override;
/** Autodetects tools from this device and returns them as a navigation tool storage.
* @return Returns the detected tools. Returns an empty storage if no tools are present
* or if detection is not possible
*/
mitk::NavigationToolStorage::Pointer AutoDetectTools() override;
protected:
typedef std::vector<std::string> NDITrackingVolumeContainerType; ///< vector of tracking volumes
typedef std::vector<int> TrackingVolumeDimensionType; ///< List of the supported tracking volume dimensions.
/**
* \brief Get number of supported tracking volumes, a vector containing the supported volumes and
* a vector containing the signed dimensions in mm. For each volume 10 boundaries are stored in the order of
* the supported volumes (see AURORA API GUIDE: SFLIST p.54).
**/
virtual bool GetSupportedVolumes(unsigned int* numberOfVolumes, NDITrackingVolumeContainerType* volumes, TrackingVolumeDimensionType* volumesDimensions);
/**
* \brief Sets the desired tracking volume. Returns true if the volume type could be set. It is set in the OpenConnection() Method and sets the tracking volume out of m_Data.
* @throw mitk::IGTHardwareException Throws an IGT hardware exception if the volume could not be set.
**/
virtual bool SetVolume(mitk::TrackingDeviceData volume);
/**
* \brief Add a passive 6D tool to the list of tracked tools. This method is used by AddTool
* @throw mitk::IGTHardwareException Throws an exception if there are errors while adding the tool.
* \warning adding tools is not possible in tracking mode, only in setup and ready.
*/
virtual bool InternalAddTool(NDIPassiveTool* tool);
/* Methods for NDIProtocol friend class */
virtual void InvalidateAll(); ///< invalidate all tools
NDIPassiveTool* GetInternalTool(std::string portHandle); ///< returns the tool object that has been assigned the port handle or nullptr if no tool can be found
/**
* \brief free all port handles that need to be freed
*
* This method retrieves a list of all port handles that need to be freed (e.g. tool got disconnected)
* and frees the handles at the tracking device and it removes the tools from the internal tool list
* \warning This method can remove TrackingTools from the tool list! After calling this method, GetTool(i) could return
* a different tool, because tool indices could have changed.
* @throw mitk::IGTHardwareException Throws an exception if there are errors while communicating with the device.
* \return returns NDIOKAY if everything was sucessfull, returns an error code otherwise
*/
NDIErrorCode FreePortHandles();
NDIErrorCode Send(const std::string* message, bool addCRC = true); ///< Send message to tracking device
NDIErrorCode Receive(std::string* answer, unsigned int numberOfBytes); ///< receive numberOfBytes bytes from tracking device
NDIErrorCode ReceiveByte(char* answer); ///< lightweight receive function, that reads just one byte
NDIErrorCode ReceiveLine(std::string* answer); ///< receive characters until the first LF (The LF is included in the answer string)
void ClearSendBuffer(); ///< empty send buffer of serial communication interface
void ClearReceiveBuffer(); ///< empty receive buffer of serial communication interface
const std::string CalcCRC(const std::string* input); ///< returns the CRC16 for input as a std::string
public:
/**
* \brief TrackTools() continuously polls serial interface for new 6d tool positions until StopTracking is called.
*
* Continuously tracks the 6D position of all tools until StopTracking() is called.
* This function is executed by the tracking thread (through StartTracking() and ThreadStartTracking()).
* It should not be called directly.
* @throw mitk::IGTHardwareException Throws an exception if there are errors while tracking the tools.
*/
virtual void TrackTools();
/**
* \brief continuously polls serial interface for new 3D marker positions until StopTracking is called.
*
* Continuously tracks the 3D position of all markers until StopTracking() is called.
* This function is executed by the tracking thread (through StartTracking() and ThreadStartTracking()).
* It should not be called directly.
*/
virtual void TrackMarkerPositions();
/**
* \brief continuously polls serial interface for new 3D marker positions and 6D tool positions until StopTracking is called.
*
* Continuously tracks the 3D position of all markers and the 6D position of all tools until StopTracking() is called.
* This function is executed by the tracking thread (through StartTracking() and ThreadStartTracking()).
* It should not be called directly.
*/
virtual void TrackToolsAndMarkers();
/**
- * \brief static start method for the tracking thread.
+ * \brief start method for the tracking thread.
*/
- static ITK_THREAD_RETURN_TYPE ThreadStartTracking(void* data);
+ void ThreadStartTracking();
protected:
NDITrackingDevice(); ///< Constructor
~NDITrackingDevice() override; ///< Destructor
std::string m_DeviceName;///< Device Name
PortNumber m_PortNumber; ///< COM Port Number
BaudRate m_BaudRate; ///< COM Port Baud Rate
DataBits m_DataBits; ///< Number of Data Bits per token
Parity m_Parity; ///< Parity mode for communication
StopBits m_StopBits; ///< number of stop bits per token
HardwareHandshake m_HardwareHandshake; ///< use hardware handshake for serial port connection
///< which tracking volume is currently used (if device supports multiple volumes) (\warning This parameter is not used yet)
IlluminationActivationRate m_IlluminationActivationRate; ///< update rate of IR illuminator for Polaris
DataTransferMode m_DataTransferMode; ///< use TX (text) or BX (binary) (\warning currently, only TX mode is supported)
Tool6DContainerType m_6DTools; ///< list of 6D tools
- itk::FastMutexLock::Pointer m_ToolsMutex; ///< mutex for coordinated access of tool container
+ mutable std::mutex m_ToolsMutex; ///< mutex for coordinated access of tool container
mitk::SerialCommunication::Pointer m_SerialCommunication; ///< serial communication interface
- itk::FastMutexLock::Pointer m_SerialCommunicationMutex; ///< mutex for coordinated access of serial communication interface
+ std::mutex m_SerialCommunicationMutex; ///< mutex for coordinated access of serial communication interface
NDIProtocol::Pointer m_DeviceProtocol; ///< create and parse NDI protocol strings
- itk::MultiThreader::Pointer m_MultiThreader; ///< creates tracking thread that continuously polls serial interface for new tracking data
- int m_ThreadID; ///< ID of tracking thread
+ std::thread m_Thread; ///< ID of tracking thread
OperationMode m_OperationMode; ///< tracking mode (6D tool tracking, 3D marker tracking,...)
- itk::FastMutexLock::Pointer m_MarkerPointsMutex; ///< mutex for marker point data container
+ std::mutex m_MarkerPointsMutex; ///< mutex for marker point data container
MarkerPointContainerType m_MarkerPoints; ///< container for markers (3D point tracking mode)
};
} // namespace mitk
#endif /* MITKNDITRACKINGDEVICE_H_HEADER_INCLUDED_C1C2FCD2 */
diff --git a/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingDevice.cpp b/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingDevice.cpp
index b9fe449077..9b78fca04a 100644
--- a/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingDevice.cpp
+++ b/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingDevice.cpp
@@ -1,504 +1,501 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkOpenIGTLinkTrackingDevice.h"
#include "mitkOpenIGTLinkTrackingTool.h"
#include "mitkIGTConfig.h"
#include "mitkIGTTimeStamp.h"
#include "mitkIGTHardwareException.h"
#include "mitkTrackingTypes.h"
#include <itksys/SystemTools.hxx>
#include <iostream>
-#include <itkMutexLockHolder.h>
#include <itkCommand.h>
#include <mitkOpenIGTLinkTypeInformation.h>
#include <vtkConeSource.h>
//sleep headers
#include <chrono>
#include <thread>
-typedef itk::MutexLockHolder<itk::FastMutexLock> MutexLockHolder;
-
mitk::OpenIGTLinkTrackingDevice::OpenIGTLinkTrackingDevice() : mitk::TrackingDevice(), m_UpdateRate(60)
{
//set the type of this tracking device
this->m_Data = mitk::OpenIGTLinkTypeInformation::GetDeviceDataOpenIGTLinkTrackingDeviceConnection();
m_OpenIGTLinkClient = mitk::IGTLClient::New(true);
m_OpenIGTLinkClient->SetName("OpenIGTLink Tracking Device");
m_OpenIGTLinkClient->EnableNoBufferingMode(false);
m_IGTLDeviceSource = mitk::IGTLTrackingDataDeviceSource::New();
m_IGTLDeviceSource->SetIGTLDevice(m_OpenIGTLinkClient);
}
mitk::OpenIGTLinkTrackingDevice::~OpenIGTLinkTrackingDevice()
{
}
int mitk::OpenIGTLinkTrackingDevice::GetPortNumber()
{
return m_OpenIGTLinkClient->GetPortNumber();
}
bool mitk::OpenIGTLinkTrackingDevice::AutoDetectToolsAvailable()
{
return true;
}
mitk::NavigationToolStorage::Pointer mitk::OpenIGTLinkTrackingDevice::AutoDetectTools()
{
mitk::NavigationToolStorage::Pointer returnValue = mitk::NavigationToolStorage::New();
if (m_OpenIGTLinkClient->GetPortNumber() == -1)
{
MITK_WARN << "Connection not initialized, aborting (invalid port number).";
return mitk::NavigationToolStorage::New();
}
//open connection
try
{
m_IGTLDeviceSource->Connect();
m_IGTLDeviceSource->StartCommunication();
}
catch (std::runtime_error &e)
{
MITK_WARN << "AutoDetection: Open IGT Link device retruned an error while trying to connect: " << e.what();
return mitk::NavigationToolStorage::New();
}
//get a message to find out type
m_IGTLDeviceSource->SettrackingDataType(mitk::IGTLTrackingDataDeviceSource::UNKNOWN);
mitk::IGTLMessage::Pointer receivedMessage = ReceiveMessage(100);
const char* msgType = receivedMessage->GetIGTLMessageType();
if (std::string(msgType).empty())
{
MITK_INFO << "Did not receive a message. Do you have to start the stream manually at the server?";
MITK_INFO << "Waiting for 10 seconds ...";
receivedMessage = ReceiveMessage(10000);
msgType = receivedMessage->GetIGTLMessageType();
}
MITK_INFO << "################# got message type: " << msgType;
mitk::OpenIGTLinkTrackingDevice::TrackingMessageType type = GetMessageTypeFromString(msgType);
switch (type)
{
case UNKNOWN:
m_IGTLDeviceSource->SettrackingDataType(mitk::IGTLTrackingDataDeviceSource::UNKNOWN);
break;
case TDATA:
m_IGTLDeviceSource->SettrackingDataType(mitk::IGTLTrackingDataDeviceSource::TDATA);
break;
case QTDATA:
m_IGTLDeviceSource->SettrackingDataType(mitk::IGTLTrackingDataDeviceSource::QTDATA);
break;
case TRANSFORM:
m_IGTLDeviceSource->SettrackingDataType(mitk::IGTLTrackingDataDeviceSource::TRANSFORM);
break;
}
returnValue = DiscoverToolsAndConvertToNavigationTools(type);
//close connection
try
{
m_IGTLDeviceSource->StopCommunication();
m_IGTLDeviceSource->Disconnect();
}
catch (std::runtime_error &e)
{
MITK_WARN << "AutoDetection: Open IGT Link device retruned an error while trying to disconnect: " << e.what();
return mitk::NavigationToolStorage::New();
}
return returnValue;
}
mitk::NavigationToolStorage::Pointer mitk::OpenIGTLinkTrackingDevice::DiscoverToolsAndConvertToNavigationTools(mitk::OpenIGTLinkTrackingDevice::TrackingMessageType type, int NumberOfMessagesToWait)
{
MITK_INFO << "Start discovering tools by " << type << " messages";
mitk::NavigationToolStorage::Pointer returnValue = mitk::NavigationToolStorage::New();
std::map<std::string, int> toolNameMap;
for (int j = 0; j<NumberOfMessagesToWait; j++)
{
std::this_thread::sleep_for(std::chrono::milliseconds(20));
m_IGTLDeviceSource->Update();
switch (type)
{
case TRANSFORM:
{
igtl::TransformMessage::Pointer msg = dynamic_cast<igtl::TransformMessage*>(m_IGTLDeviceSource->GetOutput()->GetMessage().GetPointer());
if (msg == nullptr || msg.IsNull()) { MITK_INFO << "Received message is invalid / null. Skipping.."; continue; }
int count = toolNameMap[msg->GetDeviceName()];
if (count == 0) { toolNameMap[msg->GetDeviceName()] = 1; }
else { toolNameMap[msg->GetDeviceName()]++; }
}
break;
case TDATA:
{
igtl::TrackingDataMessage::Pointer msg = dynamic_cast<igtl::TrackingDataMessage*>(m_IGTLDeviceSource->GetOutput()->GetMessage().GetPointer());
if (msg == nullptr || msg.IsNull()) { MITK_INFO << "Received message is invalid / null. Skipping.."; continue; }
for (int k = 0; k < msg->GetNumberOfTrackingDataElements(); k++)
{
igtl::TrackingDataElement::Pointer tde;
msg->GetTrackingDataElement(k, tde);
if (tde.IsNotNull())
{
int count = toolNameMap[tde->GetName()];
if (count == 0) { toolNameMap[tde->GetName()] = 1; }
else { toolNameMap[tde->GetName()]++; }
}
}
}
break;
default:
MITK_WARN << "Only TRANSFORM and TDATA is currently supported, skipping!";
break;
}
}
int i = 0;
for (std::map<std::string, int>::iterator it = toolNameMap.begin(); it != toolNameMap.end(); ++it)
{
MITK_INFO << "Found tool: " << it->first;
std::stringstream name;
name << it->first;
std::stringstream identifier;
identifier << "AutoDetectedTool-" << i;
i++;
mitk::NavigationTool::Pointer newTool = ConstructDefaultOpenIGTLinkTool(name.str(), identifier.str());
returnValue->AddTool(newTool);
}
return returnValue;
}
std::string mitk::OpenIGTLinkTrackingDevice::GetHostname()
{
return m_OpenIGTLinkClient->GetHostname();
}
void mitk::OpenIGTLinkTrackingDevice::SetPortNumber(int portNumber)
{
m_OpenIGTLinkClient->SetPortNumber(portNumber);
}
void mitk::OpenIGTLinkTrackingDevice::SetHostname(std::string hostname)
{
m_OpenIGTLinkClient->SetHostname(hostname);
}
bool mitk::OpenIGTLinkTrackingDevice::IsDeviceInstalled()
{
return true;
}
mitk::TrackingTool* mitk::OpenIGTLinkTrackingDevice::AddTool(const char*, const char*)
{
mitk::OpenIGTLinkTrackingTool::Pointer t;// = mitk::OpenIGTLinkTrackingTool::New();
//TODO: Implement
if (this->InternalAddTool(t) == false)
return nullptr;
return t.GetPointer();
}
bool mitk::OpenIGTLinkTrackingDevice::InternalAddTool(OpenIGTLinkTrackingTool::Pointer tool)
{
m_AllTools.push_back(tool);
return true;
}
bool mitk::OpenIGTLinkTrackingDevice::DiscoverTools(int waitingTime)
{
if (m_OpenIGTLinkClient->GetPortNumber() == -1)
{
MITK_WARN << "Connection not initialized, aborting (invalid port number).";
return false;
}
try
{
m_IGTLDeviceSource->Connect();
m_IGTLDeviceSource->StartCommunication();
}
catch (std::runtime_error &e)
{
MITK_WARN << "Open IGT Link device retruned an error while trying to connect: " << e.what();
return false;
}
mitk::IGTLMessage::Pointer receivedMessage = ReceiveMessage(waitingTime);
//check the tracking stream for the number and type of tools
//igtl::MessageBase::Pointer receivedMessage = m_OpenIGTLinkClient->GetNextMessage();
if (receivedMessage.IsNull())
{
MITK_WARN << "No message was received. Is there really a server?";
return false;
}
else if (!receivedMessage->IsDataValid())
{
MITK_WARN << "Received invalid message.";
return false;
}
const char* msgType = receivedMessage->GetIGTLMessageType();
mitk::OpenIGTLinkTrackingDevice::TrackingMessageType type = GetMessageTypeFromString(msgType);
mitk::NavigationToolStorage::Pointer foundTools = this->DiscoverToolsAndConvertToNavigationTools(type);
if (foundTools.IsNull() || (foundTools->GetToolCount() == 0)) { return false; }
for (unsigned int i = 0; i < foundTools->GetToolCount(); i++) { AddNewToolForName(foundTools->GetTool(i)->GetToolName(), i); }
MITK_INFO << "Found tools: " << foundTools->GetToolCount();
return true;
}
mitk::IGTLMessage::Pointer mitk::OpenIGTLinkTrackingDevice::ReceiveMessage(int waitingTime)
{
mitk::IGTLMessage::Pointer receivedMessage;
//send a message to the server: start tracking stream
mitk::IGTLMessageFactory::Pointer msgFactory = m_OpenIGTLinkClient->GetMessageFactory();
std::string message[2] = {"STT_QTDATA","STT_TDATA"};
for (int i = 0; i < 2; i++)
{
igtl::MessageBase::Pointer sttMsg = msgFactory->CreateInstance(message[i]);
//TODO: Fix this to dynamically get this from GUI
((igtl::StartTrackingDataMessage*)sttMsg.GetPointer())->SetResolution(m_UpdateRate);
m_OpenIGTLinkClient->SendMessage(mitk::IGTLMessage::New(sttMsg));
}
std::chrono::high_resolution_clock::time_point time = std::chrono::high_resolution_clock::now();
std::chrono::milliseconds d = std::chrono::milliseconds(waitingTime);
while (!(receivedMessage.IsNotNull() && receivedMessage->IsDataValid()))
{
m_IGTLDeviceSource->Update();
receivedMessage = m_IGTLDeviceSource->GetOutput();
if ((time + d) < std::chrono::high_resolution_clock::now())
break;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
return receivedMessage;
}
void mitk::OpenIGTLinkTrackingDevice::AddNewToolForName(std::string name, int i)
{
mitk::OpenIGTLinkTrackingTool::Pointer newTool = mitk::OpenIGTLinkTrackingTool::New();
if (name == "") //if no name was given create a default name
{
std::stringstream defaultName;
defaultName << "OpenIGTLinkTool#" << i;
name = defaultName.str();
}
MITK_INFO << "Added tool " << name << " to tracking device.";
newTool->SetToolName(name);
InternalAddTool(newTool);
}
mitk::NavigationTool::Pointer mitk::OpenIGTLinkTrackingDevice::ConstructDefaultOpenIGTLinkTool(std::string name, std::string identifier)
{
mitk::NavigationTool::Pointer newTool = mitk::NavigationTool::New();
newTool->GetDataNode()->SetName(name);
newTool->SetIdentifier(identifier);
newTool->SetTrackingDeviceType(mitk::OpenIGTLinkTypeInformation::GetDeviceDataOpenIGTLinkTrackingDeviceConnection().Line);
return newTool;
}
void mitk::OpenIGTLinkTrackingDevice::UpdateTools()
{
if (this->GetState() != Tracking)
{
MITK_ERROR << "Method was called in the wrong state, something went wrong!";
return;
}
m_IGTLMsgToNavDataFilter->Update();
for (std::size_t j = 0; j < m_IGTLMsgToNavDataFilter->GetNumberOfIndexedOutputs(); ++j)
{
mitk::NavigationData::Pointer currentNavData = m_IGTLMsgToNavDataFilter->GetOutput(j);
const char* name = currentNavData->GetName();
for (std::size_t i = 0; i < m_AllTools.size(); i++)
{
if (strcmp(m_AllTools.at(i)->GetToolName(), name) == 0)
{
m_AllTools.at(i)->SetDataValid(currentNavData->IsDataValid());
m_AllTools.at(i)->SetPosition(currentNavData->GetPosition());
m_AllTools.at(i)->SetOrientation(currentNavData->GetOrientation());
m_AllTools.at(i)->SetIGTTimeStamp(currentNavData->GetIGTTimeStamp());
}
}
}
}
bool mitk::OpenIGTLinkTrackingDevice::StartTracking()
{
//check tracking state
if (this->GetState() != Ready)
{
MITK_WARN << "Cannot start tracking, device is not ready!";
return false;
}
try
{
m_IGTLDeviceSource->StartCommunication();
//send a message to the server: start tracking stream
mitk::IGTLMessageFactory::Pointer msgFactory = m_OpenIGTLinkClient->GetMessageFactory();
std::string message = "STT_TDATA";
//m_OpenIGTLinkClient->SendMessage(msgFactory->CreateInstance(message));
}
catch (std::runtime_error &e)
{
MITK_WARN << "Open IGT Link device retruned an error while starting communication: " << e.what();
return false;
}
//create internal igtl pipeline
m_IGTLMsgToNavDataFilter = mitk::IGTLMessageToNavigationDataFilter::New();
m_IGTLMsgToNavDataFilter->SetNumberOfExpectedOutputs(this->GetToolCount());
m_IGTLMsgToNavDataFilter->ConnectTo(m_IGTLDeviceSource);
//connect itk events
typedef itk::SimpleMemberCommand< mitk::OpenIGTLinkTrackingDevice > CurCommandType;
CurCommandType::Pointer messageReceivedCommand = CurCommandType::New();
messageReceivedCommand->SetCallbackFunction(this, &mitk::OpenIGTLinkTrackingDevice::UpdateTools);
m_MessageReceivedObserverTag = m_OpenIGTLinkClient->AddObserver(mitk::MessageReceivedEvent(), messageReceivedCommand);
m_OpenIGTLinkClient->EnableNoBufferingMode(true);
this->SetState(Tracking);
return true;
}
bool mitk::OpenIGTLinkTrackingDevice::StopTracking()
{
//check tracking state
if (this->GetState() != Tracking)
{
MITK_WARN << "Cannot open connection, device is already connected!";
return false;
}
m_OpenIGTLinkClient->RemoveObserver(m_MessageReceivedObserverTag); //disconnect itk events
try
{
m_IGTLDeviceSource->StopCommunication();
}
catch (std::runtime_error &e)
{
MITK_WARN << "Open IGT Link device retruned an error while stopping communication: " << e.what();
return false;
}
m_OpenIGTLinkClient->EnableNoBufferingMode(false);
this->SetState(Ready);
return true;
}
unsigned int mitk::OpenIGTLinkTrackingDevice::GetToolCount() const
{
return (unsigned int)this->m_AllTools.size();
}
mitk::TrackingTool* mitk::OpenIGTLinkTrackingDevice::GetTool(unsigned int toolNumber) const
{
if (toolNumber >= this->GetToolCount())
return nullptr;
else
return this->m_AllTools[toolNumber];
}
bool mitk::OpenIGTLinkTrackingDevice::OpenConnection()
{
//check tracking state
if (this->GetState() != Setup)
{
MITK_WARN << "Cannot open connection, device is already connected!";
return false;
}
try
{
m_IGTLDeviceSource->Connect();
}
catch (std::runtime_error &e)
{
MITK_WARN << "Open IGT Link device retruned an error while trying to connect: " << e.what();
return false;
}
this->SetState(Ready);
return true;
}
bool mitk::OpenIGTLinkTrackingDevice::CloseConnection()
{
//check tracking state
if (this->GetState() != Ready)
{
MITK_WARN << "Cannot close connection, device is in the wrong state!";
return false;
}
try
{
m_IGTLDeviceSource->Disconnect();
}
catch (std::runtime_error &e)
{
MITK_WARN << "Open IGT Link device retruned an error while trying to disconnect: " << e.what();
return false;
}
this->SetState(Setup);
return true;
}
std::vector<mitk::OpenIGTLinkTrackingTool::Pointer> mitk::OpenIGTLinkTrackingDevice::GetAllTools()
{
return this->m_AllTools;
}
mitk::OpenIGTLinkTrackingDevice::TrackingMessageType mitk::OpenIGTLinkTrackingDevice::GetMessageTypeFromString(const char* messageTypeString)
{
if (strcmp(messageTypeString, "TDATA") == 0)
{
return mitk::OpenIGTLinkTrackingDevice::TrackingMessageType::TDATA;
}
else if (strcmp(messageTypeString, "QTDATA") == 0)
{
return mitk::OpenIGTLinkTrackingDevice::TrackingMessageType::QTDATA;
}
else if (strcmp(messageTypeString, "TRANSFORM") == 0)
{
return mitk::OpenIGTLinkTrackingDevice::TrackingMessageType::TRANSFORM;
}
else
{
return mitk::OpenIGTLinkTrackingDevice::TrackingMessageType::UNKNOWN;
}
}
diff --git a/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingDevice.h b/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingDevice.h
index e13411d464..12f66a5cac 100644
--- a/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingDevice.h
+++ b/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingDevice.h
@@ -1,175 +1,174 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKOPENIGTLINKTRACKINGDEVICE_H_HEADER_INCLUDED_
#define MITKOPENIGTLINKTRACKINGDEVICE_H_HEADER_INCLUDED_
#include <mitkIGTConfig.h>
#include <mitkTrackingDevice.h>
#include <mitkOpenIGTLinkTrackingTool.h>
#include <mitkIGTLClient.h>
#include <mitkIGTLDeviceSource.h>
#include <mitkIGTLMessageToNavigationDataFilter.h>
-#include <itkMultiThreader.h>
#include <igtlQuaternionTrackingDataMessage.h>
#include <igtlTrackingDataMessage.h>
#include <igtlTransformMessage.h>
#include "mitkIGTLTrackingDataDeviceSource.h"
namespace mitk
{
/** Documentation:
* \brief An object of this class represents the MicronTracker device. You can add tools to this
* device, then open the connection and start tracking. The tracking device will then
* continuously update the tool coordinates.
* \ingroup IGT
*/
class MITKIGT_EXPORT OpenIGTLinkTrackingDevice : public TrackingDevice
{
public:
mitkClassMacro(OpenIGTLinkTrackingDevice, TrackingDevice);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** Sets the port number for the Open IGT Link connection. Default value is -1 (invalid). */
void SetPortNumber(int portNumber);
/** Sets the hostname for the Open IGT Link connection. Default value is 127.0.0.1 (localhost). */
void SetHostname(std::string hostname);
int GetPortNumber();
std::string GetHostname();
/**
* \brief Starts the tracking.
* \return Returns true if the tracking is started. Throws an exception if an error occures.
* @throw mitk::IGTHardwareException Throws an exception if there is an error during start tracking.
*/
bool StartTracking() override;
/**
* \brief Stops the tracking.
* \return Returns true if the tracking is stopped.
*/
bool StopTracking() override;
/**
* \brief Opens the connection to the device. This have to be done before the tracking is started.
* @throw mitk::IGTHardwareException Throws an exception if there is an error during open connection.
*/
bool OpenConnection() override;
/**
* \brief Closes the connection and clears all resources.
*/
bool CloseConnection() override;
/**
* \return Returns the number of tools which have been added to the device.
*/
unsigned int GetToolCount() const override;
/**
* \param toolNumber The number of the tool which should be given back.
* \return Returns the tool which the number "toolNumber". Returns nullptr, if there is
* no tool with this number.
*/
TrackingTool* GetTool(unsigned int toolNumber) const override;
/**
* \brief Discover the tools available from the connected OpenIGTLink device and adds these tools to this tracking device. Therefore, a connection
* is opened, the tools are discovered and added.
* \param WaitingTime Defines how long the method waits for an answer from the server (in milliseconds). Default value is 10000 (10 seconds).
* \return Returns true if the connection was established and the tools were discovered successfully and - if at least one tool was found - were added to this device.
* Retruns false if no valid connection is available.
*/
bool DiscoverTools(int WaitingTime = 10000);
/**
* \brief Create a new OpenIGTLink tool with toolName and fileName and add it to the list of tools
*
* Note that tools are usually provided by the OpenIGTLink connection. In most cases, the method DiscoverTools() should be used
* instead which automatically finds the provided tools. If you use this method to manually add tools be sure that you add the
* same number and type of tools that are provided by the connected device. Otherwise problems might occur when you try to start
* tracking.
*/
mitk::TrackingTool* AddTool(const char* toolName, const char* fileName);
/** @return Returns true if this device can autodetects its tools. */
bool AutoDetectToolsAvailable() override;
/** Autodetects tools from the current OpenIGTLink connection and returns them as a navigation tool storage.
* @return Returns the detected tools. Returns an empty storage if no tools are present
* or if OpenIGTLink Connection is not possible
*/
mitk::NavigationToolStorage::Pointer AutoDetectTools() override;
bool IsDeviceInstalled() override;
itkSetMacro(UpdateRate, int); ///< Sets the update rate of the device in fps. Default value is 60 fps.
itkGetConstMacro(UpdateRate, int); ///< Returns the update rate of the device in fps
protected:
OpenIGTLinkTrackingDevice();
~OpenIGTLinkTrackingDevice() override;
/**
* \brief Adds a tool to the tracking device.
*
* \param tool The tool which will be added.
* \return Returns true if the tool has been added, false otherwise.
*/
bool InternalAddTool(OpenIGTLinkTrackingTool::Pointer tool);
/** Updates the tools from the open IGT link connection. Is called every time a message received event is invoked.*/
void UpdateTools();
unsigned long m_MessageReceivedObserverTag;
/** Receives one message from the OpenIGTLink connection. Starts the tracking stream if required.
*/
mitk::IGTLMessage::Pointer ReceiveMessage(int waitingTime);
/**
* \return Returns all tools of the tracking device.
*/
std::vector<OpenIGTLinkTrackingTool::Pointer> GetAllTools();
//OpenIGTLink connection class
mitk::IGTLClient::Pointer m_OpenIGTLinkClient;
//OpenIGTLink pipeline
mitk::IGTLTrackingDataDeviceSource::Pointer m_IGTLDeviceSource;
mitk::IGTLMessageToNavigationDataFilter::Pointer m_IGTLMsgToNavDataFilter;
std::vector<OpenIGTLinkTrackingTool::Pointer> m_AllTools; ///< vector holding all tools
int m_UpdateRate; ///< holds the update rate in FPS (will be set automatically when the OpenIGTLink connection is established)
private:
enum TrackingMessageType
{
TDATA, TRANSFORM, QTDATA, UNKNOWN
};
mitk::OpenIGTLinkTrackingDevice::TrackingMessageType GetMessageTypeFromString(const char* messageTypeString);
/** Discovers tools from the OpenIGTLink connection and converts them to MITK navigation tool objects.
@return Returns a navigation tool storage holding all found tools. Returns an empty storage if no tools were found or if there was an error.*/
mitk::NavigationToolStorage::Pointer DiscoverToolsAndConvertToNavigationTools(mitk::OpenIGTLinkTrackingDevice::TrackingMessageType type, int NumberOfMessagesToWait = 50);
void AddNewToolForName(std::string name, int i);
mitk::NavigationTool::Pointer ConstructDefaultOpenIGTLinkTool(std::string name, std::string identifier);
};
}//mitk
#endif /* MITKOpenIGTLinkTRACKINGDEVICE_H_HEADER_INCLUDED_ */
diff --git a/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingTool.h b/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingTool.h
index 14d573c261..9b305f97ec 100644
--- a/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingTool.h
+++ b/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingTool.h
@@ -1,42 +1,41 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKOpenIGTLinkTrackingTOOL_H_HEADER_INCLUDED_
#define MITKOpenIGTLinkTrackingTOOL_H_HEADER_INCLUDED_
#include <mitkTrackingTool.h>
-#include <itkFastMutexLock.h>
namespace mitk
{
class OpenIGTLinkTrackingDevice;
/** Documentation:
* \brief An object of this class represents a OpenIGTLink tracking tool.
* A tool has to be added to a tracking device which will then
* continuously update the tool coordinates.
* \ingroup IGT
*/
class MITKIGT_EXPORT OpenIGTLinkTrackingTool : public TrackingTool
{
public:
friend class OpenIGTLinkTrackingTrackingDevice;
mitkClassMacro(OpenIGTLinkTrackingTool, TrackingTool);
itkFactorylessNewMacro(Self)
protected:
itkCloneMacro(Self)
OpenIGTLinkTrackingTool();
~OpenIGTLinkTrackingTool() override;
};
}//mitk
#endif // MITKOpenIGTLinkTrackingTOOL_H_HEADER_INCLUDED_
diff --git a/Modules/IGT/TrackingDevices/mitkOptitrackTrackingDevice.cpp b/Modules/IGT/TrackingDevices/mitkOptitrackTrackingDevice.cpp
index bf4d819264..7ab6f3c9ad 100644
--- a/Modules/IGT/TrackingDevices/mitkOptitrackTrackingDevice.cpp
+++ b/Modules/IGT/TrackingDevices/mitkOptitrackTrackingDevice.cpp
@@ -1,766 +1,734 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkOptitrackTrackingDevice.h"
#include <mitkOptitrackErrorMessages.h>
#ifdef MITK_USE_OPTITRACK_TRACKER
/**
* \brief API library header for Optitrack Systems
*/
#include <NPTrackingTools.h>
//=======================================================
// Static method: IsDeviceInstalled
//=======================================================
bool mitk::OptitrackTrackingDevice::IsDeviceInstalled()
{
return true;
}
//=======================================================
// Constructor
//=======================================================
mitk::OptitrackTrackingDevice::OptitrackTrackingDevice()
: mitk::TrackingDevice(),
m_initialized(false)
{
- // Set the MultiThread and Mutex
- this->m_MultiThreader = itk::MultiThreader::New();
- this->m_ToolsMutex = itk::FastMutexLock::New();
-
//Set the mitk device information
SetData(mitk::DeviceDataNPOptitrack);
//Clear List of tools
this->m_AllTools.clear();
}
//=======================================================
// Destructor
//=======================================================
mitk::OptitrackTrackingDevice::~OptitrackTrackingDevice()
{
MITK_DEBUG << "Deleting OptitrackTrackingDevice";
int result;
// If device is in Tracking mode, stop the Tracking firts
if (this->GetState() == mitk::TrackingDevice::Tracking)
{
MITK_DEBUG << "OptitrackTrackingDevice in Tracking State -> Stopping Tracking";
result = this->StopTracking();
if(result == NPRESULT_SUCCESS){
MITK_INFO << "OptitrackTrackingDevice Stopped";
}
else
{
MITK_INFO << "Error during Stopping";
mitkThrowException(mitk::IGTException) << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(result);
}
}
// If device is Ready, Close the connection to device and release the memory
if (this->GetState() == mitk::TrackingDevice::Ready)
{
MITK_DEBUG << "OptitrackTrackingDevice in Ready State -> Closing the Connection";
result = this->CloseConnection();
if(result)
{
MITK_INFO << "OptitrackTrackingDevice Connection closed";
}
else
{
MITK_DEBUG << "Error during Closing Connection";
mitkThrowException(mitk::IGTException) << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(result);
}
}
// Set the device off
m_initialized = false;
// Change State to Setup
this->SetState(mitk::TrackingDevice::Setup);
MITK_DEBUG <<"OptitrackTrackingDevice deleted successfully";
}
//=======================================================
// OpenConnection
//=======================================================
bool mitk::OptitrackTrackingDevice::OpenConnection()
{
// Not initialize the system twice.
if(!m_initialized)
{
MITK_DEBUG << "Initialize Optitrack Tracking System";
if( this->InitializeCameras() )
{
m_initialized = true; // Set the initialized variable to true
this->SetState(mitk::TrackingDevice::Ready);
if(this->m_calibrationPath.empty()){
MITK_INFO << "Numer of connected cameras = " << TT_CameraCount();
MITK_WARN << "Attention: No calibration File defined !!";
return m_initialized;
}
else
{
this->LoadCalibration();
}
}
else
{
m_initialized = false; // Set the initialized variable to false
this->SetState(mitk::TrackingDevice::Setup); // Set the State to Setup
MITK_INFO << "Device initialization failed. Device is still in setup state";
mitkThrowException(mitk::IGTException) << "Device initialization failed. Device is still in setup state";
}
}
//this->LoadCalibration();
return m_initialized;
}
//=======================================================
// InitializeCameras
//=======================================================
bool mitk::OptitrackTrackingDevice::InitializeCameras()
{
MITK_DEBUG << "Initialize Optitrack";
int result;
result = TT_Initialize(); // Initialize the cameras
if(result == NPRESULT_SUCCESS)
{
MITK_DEBUG << "Optitrack Initialization Succeed";
return true;
}
else
{
MITK_DEBUG << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(result);
// If not succeed after OPTITRACK_ATTEMPTS times launch exception
MITK_INFO << "Optitrack Tracking System cannot be initialized \n" << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(result);
mitkThrowException(mitk::IGTException) << "Optitrack Tracking System cannot be initialized \n" << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(result);
return false;
}
}
//=======================================================
// LoadCalibration
//=======================================================
bool mitk::OptitrackTrackingDevice::LoadCalibration()
{
MITK_DEBUG << "Loading System Calibration";
int resultLoadCalibration;
// Check the file path
if(this->m_calibrationPath.empty()){
MITK_INFO << "Calibration Path is empty";
mitkThrowException(mitk::IGTException) << "Calibration Path is empty";
return false;
}
// Once the system is ready and Initialized , a calibration file is loaded.
if(this->m_initialized)
{
for( int i=OPTITRACK_ATTEMPTS; i>0; i--)
{
resultLoadCalibration = TT_LoadCalibration(this->m_calibrationPath.c_str());
if(resultLoadCalibration != NPRESULT_SUCCESS)
{
MITK_DEBUG << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(resultLoadCalibration);
MITK_DEBUG << "Trying again...";
}
else
{
MITK_DEBUG << "Calibration file has been loaded successfully";
return true;
}
}
MITK_INFO << "System cannot load a calibration file";
mitkThrowException(mitk::IGTException) << "System cannot load a calibration file";
}
else
{
MITK_INFO << "System is not ready for load a calibration file because it has not been initialized yet";
mitkThrowException(mitk::IGTException) << "System is not ready for load a calibration file because it has not been initialized yet";
return false;
}
// Never reach this point
return false;
}
//=======================================================
// SetCalibrationPath
//=======================================================
void mitk::OptitrackTrackingDevice::SetCalibrationPath(std::string calibrationPath){
MITK_DEBUG << "SetcalibrationPath";
MITK_DEBUG << calibrationPath;
// Check the file path
if(calibrationPath.empty())
{
MITK_INFO << "Calibration Path is empty";
//mitkThrowException(mitk::IGTException) << "Calibration Path is empty";
return;
}
this->m_calibrationPath = calibrationPath;
MITK_INFO << "Calibration Path has been updated to: " << this->m_calibrationPath;
return;
}
//=======================================================
// CloseConnection
//=======================================================
bool mitk::OptitrackTrackingDevice::CloseConnection()
{
MITK_DEBUG << "CloseConnection";
int resultStop, resultShutdown;
if(m_initialized) // Close connection if the System was initialized first
{
if(this->GetState() == mitk::TrackingDevice::Tracking)
{
MITK_DEBUG << "Device state: Tracking -> Stoping the Tracking";
resultStop = this->StopTracking(); //Stop tracking on close
}
this->SetState(mitk::OptitrackTrackingDevice::Setup);
for( int i=OPTITRACK_ATTEMPTS; i>0; i--)
{
TT_ClearTrackableList();
resultShutdown = TT_Shutdown();
if(resultShutdown == NPRESULT_SUCCESS)
{
MITK_DEBUG << "System has been Shutdown Correctly";
Sleep(2000);
return true;
}
else
{
MITK_DEBUG << "System cannot ShutDown now. Trying again...";
}
}
MITK_INFO << "System cannot ShutDown now";
mitkThrowException(mitk::IGTException) << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(resultShutdown);
return false;
}
else
{
MITK_INFO << "System has not been initialized. Close connection cannot be done";
mitkThrowException(mitk::IGTException) << "System has not been initialized. Close connection cannot be done";
return false;
}
return false;
}
//=======================================================
// StartTracking
//=======================================================
bool mitk::OptitrackTrackingDevice::StartTracking()
{
MITK_DEBUG << "StartTracking";
bool resultIsTrackableTracked;
if (this->GetState() != mitk::TrackingDevice::Ready)
{
MITK_INFO << "System is not in State Ready -> Cannot StartTracking";
mitkThrowException(mitk::IGTException) << "System is not in State Ready -> Cannot StartTracking";
return false;
}
this->SetState(mitk::TrackingDevice::Tracking);
// Change the m_StopTracking Variable to false
- this->m_StopTrackingMutex->Lock();
+ this->m_StopTrackingMutex.lock();
this->m_StopTracking = false;
- this->m_StopTrackingMutex->Unlock();
+ this->m_StopTrackingMutex.unlock();
/******************************************************************************
###############################################################################
TODO: check the timestamp from the Optitrack API
###############################################################################
******************************************************************************/
mitk::IGTTimeStamp::GetInstance()->Start(this);
// Launch multiThreader using the Function ThreadStartTracking that executes the TrackTools() method
- m_ThreadID = m_MultiThreader->SpawnThread(this->ThreadStartTracking, this); // start a new thread that executes the TrackTools() method
+ m_Thread = std::thread(&OptitrackTrackingDevice::ThreadStartTracking, this); // start a new thread that executes the TrackTools() method
// Information for the user
if(GetToolCount() == 0) MITK_INFO << "No tools are defined";
for ( int i = 0; i < GetToolCount(); ++i) // use mutexed methods to access tool container
{
resultIsTrackableTracked = TT_IsTrackableTracked(i);
if(resultIsTrackableTracked)
{
MITK_DEBUG << "Trackable " << i << " is inside the Tracking Volume and it is Tracked";
}
else
{
MITK_DEBUG << "Trackable " << i << " is not been tracked. Check if it is inside the Tracking volume";
}
}
return true;
}
//=======================================================
// StopTracking
//=======================================================
bool mitk::OptitrackTrackingDevice::StopTracking()
{
MITK_DEBUG << "StopTracking";
if (this->GetState() == mitk::TrackingDevice::Tracking) // Only if the object is in the correct state
{
//Change the StopTracking value
- m_StopTrackingMutex->Lock(); // m_StopTracking is used by two threads, so we have to ensure correct thread handling
- m_StopTrackingMutex->Unlock();
+ m_StopTrackingMutex.lock(); // m_StopTracking is used by two threads, so we have to ensure correct thread handling
+ m_StopTrackingMutex.unlock();
this->SetState(mitk::TrackingDevice::Ready);
}
else
{
- m_TrackingFinishedMutex->Unlock();
+ m_TrackingFinishedMutex.unlock();
MITK_INFO << "System is not in State Tracking -> Cannot StopTracking";
mitkThrowException(mitk::IGTException) << "System is not in State Tracking -> Cannot StopTracking";
return false;
}
/******************************************************************************
###############################################################################
TODO: check the timestamp from the Optitrack API
###############################################################################
******************************************************************************/
mitk::IGTTimeStamp::GetInstance()->Stop(this);
- m_TrackingFinishedMutex->Unlock();
+ m_TrackingFinishedMutex.unlock();
return true;
}
//=======================================================
// ThreadStartTracking
//=======================================================
-ITK_THREAD_RETURN_TYPE mitk::OptitrackTrackingDevice::ThreadStartTracking(void* pInfoStruct)
+void mitk::OptitrackTrackingDevice::ThreadStartTracking()
{
MITK_DEBUG << "ThreadStartTracking";
-
- /* extract this pointer from Thread Info structure */
- struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct;
-
- if (pInfo == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
-
- if (pInfo->UserData == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
-
- OptitrackTrackingDevice *trackingDevice = static_cast<OptitrackTrackingDevice*>(pInfo->UserData);
-
- if (trackingDevice != nullptr)
- {
- // Call the TrackTools function in this thread
- trackingDevice->TrackTools();
- }
- else
- {
- mitkThrowException(mitk::IGTException) << "In ThreadStartTracking(): trackingDevice is nullptr";
- }
-
- trackingDevice->m_ThreadID = -1; // reset thread ID because we end the thread here
- return ITK_THREAD_RETURN_VALUE;
+ this->TrackTools();
}
//=======================================================
// GetOptitrackTool
//=======================================================
mitk::OptitrackTrackingTool* mitk::OptitrackTrackingDevice::GetOptitrackTool( unsigned int toolNumber) const
{
MITK_DEBUG << "ThreadStartTracking";
OptitrackTrackingTool* t = nullptr;
- MutexLockHolder toolsMutexLockHolder(*m_ToolsMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> toolsMutexLockHolder(m_ToolsMutex); // lock and unlock the mutex
if(toolNumber < m_AllTools.size())
{
t = m_AllTools.at(toolNumber);
}
else
{
MITK_INFO << "The tool numbered " << toolNumber << " does not exist";
mitkThrowException(mitk::IGTException) << "The tool numbered " << toolNumber << " does not exist";
}
return t;
}
//=======================================================
// GetToolCount
//=======================================================
unsigned int mitk::OptitrackTrackingDevice::GetToolCount() const
{
MITK_DEBUG << "GetToolCount";
- MutexLockHolder lock(*m_ToolsMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_ToolsMutex); // lock and unlock the mutex
return ( int)(this->m_AllTools.size());
}
//=======================================================
// TrackTools
//=======================================================
void mitk::OptitrackTrackingDevice::TrackTools()
{
MITK_DEBUG << "TrackTools";
Point3D position;
ScalarType t = 0.0;
try
{
bool localStopTracking; // Because m_StopTracking is used by two threads, access has to be guarded by a mutex. To minimize thread locking, a local copy is used here
- this->m_StopTrackingMutex->Lock(); // update the local copy of m_StopTracking
+ this->m_StopTrackingMutex.lock(); // update the local copy of m_StopTracking
localStopTracking = this->m_StopTracking;
/* lock the TrackingFinishedMutex to signal that the execution rights are now transfered to the tracking thread */
if (!localStopTracking)
{
- m_TrackingFinishedMutex->Lock();
+ m_TrackingFinishedMutex.lock();
}
- this->m_StopTrackingMutex->Unlock();
+ this->m_StopTrackingMutex.unlock();
while ((this->GetState() == mitk::TrackingDevice::Tracking) && (localStopTracking == false))
{
// For each Tracked Tool update the position and orientation
for ( int i = 0; i < GetToolCount(); ++i) // use mutexed methods to access tool container
{
OptitrackTrackingTool* currentTool = this->GetOptitrackTool(i);
if(currentTool != nullptr)
{
currentTool->updateTool();
MITK_DEBUG << "Tool number " << i << " updated position";
}
else
{
MITK_DEBUG << "Get data from tool number " << i << " failed";
mitkThrowException(mitk::IGTException) << "Get data from tool number " << i << " failed";
}
}
/* Update the local copy of m_StopTracking */
- this->m_StopTrackingMutex->Lock();
+ this->m_StopTrackingMutex.lock();
localStopTracking = m_StopTracking;
- this->m_StopTrackingMutex->Unlock();
+ this->m_StopTrackingMutex.unlock();
Sleep(OPTITRACK_FRAME_RATE);
} // tracking ends if we pass this line
- m_TrackingFinishedMutex->Unlock(); // transfer control back to main thread
+ m_TrackingFinishedMutex.unlock(); // transfer control back to main thread
}
catch(...)
{
- m_TrackingFinishedMutex->Unlock();
+ m_TrackingFinishedMutex.unlock();
this->StopTracking();
mitkThrowException(mitk::IGTException) << "Error while trying to track tools. Thread stopped.";
}
}
//=======================================================
// SetCameraParams
//=======================================================
bool mitk::OptitrackTrackingDevice::SetCameraParams(int exposure, int threshold , int intensity, int videoType )
{
MITK_DEBUG << "SetCameraParams";
if(this->m_initialized)
{
int num_cams = 0;
int resultUpdate;
bool resultSetCameraSettings;
for( int i=OPTITRACK_ATTEMPTS; i>0; i--)
{
resultUpdate = TT_Update(); // Get Update for the Optitrack API
if(resultUpdate == NPRESULT_SUCCESS)
{
MITK_DEBUG << "Update Succeed";
num_cams = TT_CameraCount();
i = 0;
}
else
{
MITK_DEBUG << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(resultUpdate);
MITK_DEBUG << "Trying again...";
Sleep(30);
}
}
// If no cameras are connected
if(num_cams == 0)
{
MITK_DEBUG << "No cameras are connected to the device";
return false;
mitkThrowException(mitk::IGTException) << "No cameras are connected to the device";
}
for(int cam = 0; cam < num_cams; cam++) // for all connected cameras
{
for( int i=OPTITRACK_ATTEMPTS; i>0; i--)
{
resultUpdate = TT_Update(); // Get Update for the Optitrack API
if(resultUpdate == NPRESULT_SUCCESS)
{
MITK_DEBUG << "Update Succeed for camera number " << cam;
resultSetCameraSettings = TT_SetCameraSettings(cam,videoType,exposure,threshold,intensity);
if(resultSetCameraSettings)
{
MITK_INFO << "Camera # "<<cam<< " params are set";
i = 0; // End attempts for camera #cam
}
else
{
MITK_DEBUG << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(resultSetCameraSettings);
if(i == 1)
mitkThrowException(mitk::IGTException) << "Camera number " << cam << " failed during setting the params. Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(resultSetCameraSettings);
}
}
else
{
MITK_DEBUG << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(resultUpdate);
MITK_DEBUG << "Update: Trying again...";
}
}
}
}
else
{
MITK_INFO << "System is not Initialized -> System is not ready to perform the Camera Parameters Setting";
mitkThrowException(mitk::IGTException) << "System is not Initialized -> System is not ready to perform the Camera Parameters Setting";
return false;
}
return true;
}
//=======================================================
// GetTool
//=======================================================
mitk::TrackingTool* mitk::OptitrackTrackingDevice::GetTool(unsigned int toolNumber) const
{
return static_cast<mitk::TrackingTool*>(GetOptitrackTool(toolNumber));
}
//=======================================================
// AddToolByFileName
//=======================================================
bool mitk::OptitrackTrackingDevice::AddToolByDefinitionFile(std::string fileName)
{
bool resultSetToolByFileName;
if(m_initialized)
{
OptitrackTrackingTool::Pointer t = OptitrackTrackingTool::New();
resultSetToolByFileName= t->SetToolByFileName(fileName);
if(resultSetToolByFileName)
{
this->m_AllTools.push_back(t);
MITK_INFO << "Added tool "<<t->GetToolName()<< ". Tool vector size is now: "<<m_AllTools.size();
return true;
}
else
{
MITK_INFO << "Tool could not be added";
mitkThrowException(mitk::IGTException) << "Tool could not be added";
return false;
}
}
else
{
MITK_INFO << "System is not Initialized -> Cannot Add tools";
mitkThrowException(mitk::IGTException) << "System is not Initialized -> Cannot Add tools";
return false;
}
}
//=======================================================
// IF Optitrack is not installed set functions to warnings
//=======================================================
#else
//=======================================================
// Static method: IsDeviceInstalled
//=======================================================
bool mitk::OptitrackTrackingDevice::IsDeviceInstalled()
{
return false;
}
//=======================================================
// Constructor
//=======================================================
mitk::OptitrackTrackingDevice::OptitrackTrackingDevice()
: mitk::TrackingDevice(),
m_initialized(false)
{
MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100);
}
//=======================================================
// Destructor
//=======================================================
mitk::OptitrackTrackingDevice::~OptitrackTrackingDevice()
{
MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100);
}
//=======================================================
// OpenConnection
//=======================================================
bool mitk::OptitrackTrackingDevice::OpenConnection()
{
MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100);
return false;
}
//=======================================================
// InitializeCameras
//=======================================================
bool mitk::OptitrackTrackingDevice::InitializeCameras()
{
MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100);
return false;
}
//=======================================================
// LoadCalibration
//=======================================================
bool mitk::OptitrackTrackingDevice::LoadCalibration()
{
MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100);
return false;
}
//=======================================================
// SetcalibrationPath
//=======================================================
void mitk::OptitrackTrackingDevice::SetCalibrationPath(std::string /*calibrationPath*/)
{
MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100);
}
//=======================================================
// CloseConnection
//=======================================================
bool mitk::OptitrackTrackingDevice::CloseConnection()
{
MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100);
return false;
}
//=======================================================
// StartTracking
//=======================================================
bool mitk::OptitrackTrackingDevice::StartTracking()
{
MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100);
return false;
}
//=======================================================
// StopTracking
//=======================================================
bool mitk::OptitrackTrackingDevice::StopTracking()
{
MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100);
return false;
}
//=======================================================
// ThreadStartTracking
//=======================================================
-ITK_THREAD_RETURN_TYPE mitk::OptitrackTrackingDevice::ThreadStartTracking(void*)
+void mitk::OptitrackTrackingDevice::ThreadStartTracking()
{
MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100);
- return 0;
}
//=======================================================
// GetOptitrackTool
//=======================================================
mitk::OptitrackTrackingTool* mitk::OptitrackTrackingDevice::GetOptitrackTool(unsigned int) const
{
MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100);
return nullptr;
}
//=======================================================
// GetToolCount
//=======================================================
unsigned int mitk::OptitrackTrackingDevice::GetToolCount() const
{
MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100);
return 0;
}
//=======================================================
// TrackTools
//=======================================================
void mitk::OptitrackTrackingDevice::TrackTools()
{
MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100);
}
//=======================================================
// SetCameraParams
//=======================================================
bool mitk::OptitrackTrackingDevice::SetCameraParams(int, int, int, int)
{
MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100);
return false;
}
//=======================================================
// GetTool
//=======================================================
mitk::TrackingTool* mitk::OptitrackTrackingDevice::GetTool(unsigned int) const
{
MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100);
return nullptr;
}
//=======================================================
// AddToolByFileName
//=======================================================
bool mitk::OptitrackTrackingDevice::AddToolByDefinitionFile(std::string)
{
MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100);
return false;
}
#endif
diff --git a/Modules/IGT/TrackingDevices/mitkOptitrackTrackingDevice.h b/Modules/IGT/TrackingDevices/mitkOptitrackTrackingDevice.h
index a575d23c11..2ddbc7cfa8 100644
--- a/Modules/IGT/TrackingDevices/mitkOptitrackTrackingDevice.h
+++ b/Modules/IGT/TrackingDevices/mitkOptitrackTrackingDevice.h
@@ -1,320 +1,301 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef OptitrackTrackingDevice_H_HEADER_INCLUDED
#define OptitrackTrackingDevice_H_HEADER_INCLUDED
#include <MitkIGTExports.h>
#include <mitkTrackingDevice.h>
-#include <itkMultiThreader.h>
#include <mitkTrackingTypes.h>
#include <mitkIGTTimeStamp.h>
-#include <itkFastMutexLock.h>
#include <itksys/SystemTools.hxx>
-#include <itkMutexLockHolder.h>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <ctime>
+#include <thread>
+#include <mutex>
/**
* \brief IGT Exceptions
*/
#include "mitkIGTIOException.h"
#include "mitkIGTTimeStamp.h"
#include "mitkIGTException.h"
/**
* \brief OptitrackTrackingTools
*/
#include "mitkOptitrackTrackingTool.h"
-
-/**
-* \brief MutexHolder to keep rest of Mutex
-*/
-typedef itk::MutexLockHolder<itk::FastMutexLock> MutexLockHolder;
-
-
-
-
namespace mitk
{
/** Documentation:
* \brief An object of this class represents the Optitrack device. You can add tools to this
* device, then open the connection and start tracking. The tracking device will then
* continuously update the tool coordinates. Remember that it will be necessary to
* to have a license for using the Optitrack System.
* See http://www.naturalpoint.com/ for details.
* \author E. Marinetto (emarinetto@hggm.es) Instituto de Investigación Sanitaria Gregorio Marañón, Madrid, Spain. & M. Noll (matthias.noll@igd.fraunhofer.de) Cognitive Computing & Medical Imaging | Fraunhofer IGD
* \ingroup IGT
*/
class MITKIGT_EXPORT OptitrackTrackingDevice : public mitk::TrackingDevice
{
friend class OptitrackTrackingTool;
public:
mitkClassMacro(OptitrackTrackingDevice, mitk::TrackingDevice);
itkNewMacro(Self);
/**
* @returns Returns true if the Optitrack tracker is installed on this build (means activated in CMAKE). False if not.
*/
bool IsDeviceInstalled() override;
// Define the Type of Tracker as DefinitionOfTool (MITK)
typedef mitk::TrackingDeviceType OptiTrackTrackingDeviceType;
/**
* \brief Open the Connection with the Tracker. Calls LoadCalibration function and set the system up with the calibration file.
* Remember that you have to set a calibration file first to open a correct connection to the Optical Tracking System.
* \return Returns true if the connection is well done. Throws an exception if an error occures related to the Optitrack API messages.
* @throw mitk::IGTException Throws an exception if InitializeCameras or LoadCalibration failed.
*/
bool OpenConnection() override;
/**
* \brief Close the Connection with the Tracker. Also CleanUp the Optitrack variables using the API: TT_CleanUp and TT_ShutDown.
* Sometimes API does not work properly and some problems during the Clean Up has been reported.
* \return Returns true if the cleaning up and shutdown worked correctly. Throws an exception if an error occures related to the Optitrack API messages.
* @throw mitk::IGTException Throws an exception if the System cannot ShutDown now or was not initialized.
*/
bool CloseConnection() override;
/**
* \brief Start to Track the tools already defined. If no tools are defined for this tracker, it returns an error.
* Tools can be added using either AddToolByDescriptionFile or AddToolsByConfigurationFiles
* \return Returns true at least one tool was defined and the tracking is correct
* @throw mitk::IGTException Throws an exception if the System is not in State Ready .
*/
bool StartTracking() override;
/**
* \brief Stop the Tracking Thread and tools will not longer be updated.
* \return Returns true if Tracking thread could be stopped.
* @throw mitk::IGTException Throws an exception if System is not in State Tracking.
*/
bool StopTracking() override;
/**
* \brief Return the tool pointer of the tool number toolNumber
* \param toolNumber The number of the tool which should be given back.
* \return Returns the tool which the number "toolNumber". Returns nullptr, if there is
* no tool with this number.
*/
TrackingTool* GetTool(unsigned int toolNumber) const override;
/**
* \brief Return the tool pointer of the tool number toolNumber
* \param toolNumber The number of the tool which should be given back.
* \return Returns the tool which the number "toolNumber". Returns nullptr, if there is
* no tool with this number.
* @throw mitk::IGTException Throws an exception if there is the required tool does not exist.
*/
OptitrackTrackingTool* GetOptitrackTool(unsigned int toolNumber) const;
/**
* \brief Returns the number of defined tools
* \return Returns the number of defined tools in the Optitrack device.
*/
unsigned int GetToolCount() const override;
/** @brief Sets the directory where the calibration file of the MicronTracker can be found. */
itkSetMacro(Exp,int);
/** @brief Gets the current calibration directory. */
itkGetMacro(Exp,int);
/** @brief Sets the directory where the calibration file of the MicronTracker can be found. */
itkSetMacro(Led,int);
/** @brief Gets the current calibration directory. */
itkGetMacro(Led,int);
/** @brief Sets the directory where the calibration file of the MicronTracker can be found. */
itkSetMacro(Thr,int);
/** @brief Gets the current calibration directory. */
itkGetMacro(Thr,int);
/** @brief Sets the file where the calibration of the OptitrackTracker can be found. */
void SetCalibrationPath(std::string calibrationPath);
/** @brief Gets the current calibration file. */
itkGetMacro(calibrationPath,std::string);
/**
* \brief Start the Tracking Thread for the tools
- * @throw mitk::IGTException Throws an exception if variable trackingDevice is nullptr
*/
- static ITK_THREAD_RETURN_TYPE ThreadStartTracking(void* data);
+ void ThreadStartTracking();
/**
* \brief Update each tool location in the list m_AllTools
* @throw mitk::IGTException Throws an exception if the getting data operation failed for a defined tool
*/
void TrackTools();
/**
* \brief Load the Calibration file to the Optitrack System and set the cameras in calibrated locations
* \return Returns true if the calibration was uploaded correctly
* @throw mitk::IGTException Throws an exception if Calibration Path is empty, the System cannot load a calibration file or System is not ready for load a calibration file because it has not been initialized yet
*/
bool LoadCalibration();
/**
* \brief Set the Cameras Exposure, Threshold and Intensity of IR LEDs. By Default it set the Video type to 4: Precision Mode for tracking
* //== VideoType:
* //== 0 = Segment Mode
* //== 1 = Grayscale Mode
* //== 2 = Object Mode
* //== 4 = Precision Mode
* //== 6 = MJPEG Mode (V100R2 only)
* \return Returns true if all cameras were set up correctly
* @throw mitk::IGTException Throws an exception if System is not Initialized
*/
bool SetCameraParams(int exposure, int threshold, int intensity, int videoType = 4);
/**
* \brief Initialize the Optitrack System
* \return Returns true if system was initialized correctly
* @throw mitk::IGTException Throws an exception if the Optitrack Tracking System cannot be initialized
*/
bool InitializeCameras();
/**
* \brief Add a new tool using a text file which described the tool.
* The file must to have the next structure
* ToolName
* \#NumberOfMarkers
* X Y Z - for the first marker
* X Y Z - for the second marker
* ...
* X Y Z - for the last marker, the number \#NumberOfMarkers
* X Y Z - for the PIVOT point
* \return Returns true if system was initialized correctly
* @throw mitk::IGTException Throws an exception if Tool could not be added or System is not Initialized
*/
bool AddToolByDefinitionFile(std::string fileName); // ^????? We should give an example of defined tool
/**
* \brief This function load a file with Tools definitions provided for the software
* \return Returns true if file is correctly loaded with all the tools
* @throw mitk::IGTException Throws an exception if there is an error during the Initialization
*/
// bool AddToolByConfigurationFil(std::string fileName); // For next release....
protected:
/**
* \brief Constructor & Destructor of the class
*/
OptitrackTrackingDevice();
~OptitrackTrackingDevice() override;
private:
/**
* \brief The calibration file path. This file is produced by TrackingTools/Motive software.
* This variable is used to determine what will be the calibration file absolute path.
*/
std::string m_calibrationPath;
/**
* \brief The Cameras Exposition
*/
int m_Exp;
/**
* \brief The Cameras LED power
*/
int m_Led;
/**
* \brief The Cameras Thr
*/
int m_Thr;
/**
* \brief Described if the system was initialized at least once during execution. This is due
* to some reported problems during the clean up , shutdown and initialization again.
*/
bool m_initialized;
/**
* \brief Vector of pointers pointing to all defined tools
*/
std::vector<mitk::OptitrackTrackingTool::Pointer> m_AllTools;
/**
* \brief Mutex for coordinated access of tool container
*/
- itk::FastMutexLock::Pointer m_ToolsMutex;
-
- /**
- * \brief MultiThreader that starts continuous tracking update
- */
- itk::MultiThreader::Pointer m_MultiThreader;
+ mutable std::mutex m_ToolsMutex;
- /**
- * \brief ThreadID number identification
- */
- int m_ThreadID;
+ std::thread m_Thread;
/* TODO:
// For Tracking
-bool AddToolByConfigurationFil(std::string fileName);
TTAPI NPRESULT TT_LoadTrackables (const char *filename); //== Load Trackables ======----
TTAPI NPRESULT TT_SaveTrackables (const char *filename); //== Save Trackables ======----
TTAPI NPRESULT TT_AddTrackables (const char *filename); //== Add Trackables ======----
TTAPI void TT_ClearTrackableList(); //== Clear all trackables =====---
TTAPI NPRESULT TT_RemoveTrackable(int Index); //== Remove single trackable ====---
TTAPI void TT_SetTrackableEnabled(int index, bool enabled); //== Set Tracking ====---
TTAPI bool TT_TrackableEnabled(int index); //== Get Tracking ====---
TTAPI int TT_TrackableMarkerCount(int index); //== Get marker count ====---
TTAPI void TT_TrackableMarker(int RigidIndex, //== Get Trackable mrkr ====---
int MarkerIndex, float *x, float *y, float *z);
//For projects
TTAPI NPRESULT TT_LoadProject(const char *filename); //== Load Project File ==========--
TTAPI NPRESULT TT_SaveProject(const char *filename); //== Save Project File ==========--
// For VRPN connection
TTAPI NPRESULT TT_StreamVRPN(bool enabled, int port);//== Start/stop VRPN Stream ===----
// For frame testing
TTAPI int TT_FrameMarkerCount(); //== Returns Frame Markers Count ---
TTAPI float TT_FrameMarkerX(int index); //== Returns X Coord of Marker -----
TTAPI float TT_FrameMarkerY(int index); //== Returns Y Coord of Marker -----
TTAPI float TT_FrameMarkerZ(int index); //== Returns Z Coord of Marker -----
TTAPI int TT_FrameMarkerLabel(int index); //== Returns Label of Marker -------
TTAPI double TT_FrameTimeStamp(); //== Time Stamp of Frame (seconds) -
// For cameras handling
TTAPI int TT_CameraCount(); //== Returns Camera Count =====-----
TTAPI float TT_CameraXLocation(int index); //== Returns Camera's X Coord =-----
TTAPI float TT_CameraYLocation(int index); //== Returns Camera's Y Coord =-----
TTAPI float TT_CameraZLocation(int index); //== Returns Camera's Z Coord =-----
TTAPI float TT_CameraOrientationMatrix(int camera, int index); //== Orientation -----
*/
};
}
#endif
diff --git a/Modules/IGT/TrackingDevices/mitkOptitrackTrackingTool.h b/Modules/IGT/TrackingDevices/mitkOptitrackTrackingTool.h
index dce6567be1..4a2f45c2ca 100644
--- a/Modules/IGT/TrackingDevices/mitkOptitrackTrackingTool.h
+++ b/Modules/IGT/TrackingDevices/mitkOptitrackTrackingTool.h
@@ -1,217 +1,213 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef OptiTrackTrackingTool_H_HEADER_INCLUDED_
#define OptiTrackTrackingTool_H_HEADER_INCLUDED_
#include <MitkIGTExports.h>
-#include <itkMultiThreader.h>
-#include "itkFastMutexLock.h"
#include "mitkTrackingDevice.h"
#include "mitkTrackingTool.h"
#include "mitkIGTTimeStamp.h"
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <itksys/SystemTools.hxx>
-#include <itkMutexLockHolder.h>
#include "mitkCommon.h"
#include <mitkTrackingTool.h>
#include <mitkVector.h>
-#include <itkFastMutexLock.h>
#include "mitkIGTException.h"
/**
* \brief Function to get the Error messages from API
*/
#include <mitkOptitrackErrorMessages.h>
namespace mitk
{
//class OptitrackTrackingDevice;
/** Documentation:
* \brief An object of this class represents the a Tool tracked by Optitrack System. You can define
* the tool by the a definition file like in the example in ****. Remember that it will be necessary to
* to have a license for using the Optitrack System.
* See http://www.naturalpoint.com/ for details.
* \author E. Marinetto (emarinetto@hggm.es) Instituto de Investigación Sanitaria Gregorio Marañón, Madrid, Spain. & M. Noll (matthias.noll@igd.fraunhofer.de) Cognitive Computing & Medical Imaging | Fraunhofer IGD
* \ingroup IGT
*/
class MITKIGT_EXPORT OptitrackTrackingTool : public TrackingTool
{
public:
friend class OptitrackTrackingDevice;
mitkClassMacro(mitk::OptitrackTrackingTool, mitk::TrackingTool);
itkNewMacro(Self);
/**
* \brief Define the tool by a calibration File.
* The file must to have the next structure. Makers locations must to have "%fe %fe %fe\n" format and in (mm). See http://www.cplusplus.com/reference/cstdio/fscanf/
* ToolName
* \#NumberOfMarkers
* X Y Z - for the first marker
* X Y Z - for the second marker
* ...
* X Y Z - for the last marker, the number \#NumberOfMarkers
* X Y Z - for the PIVOT point
* \return Returns true if the tool was set correctly
* @throw mitk::IGTException Throws an exception if there exist any problem during the configuration file reading.
*/
bool SetToolByFileName(std::string nameFile);
/**
* \brief Ask API the next number of defined tool
* \return Returns the next ID (int) for a new tool in the device list for API
* @throw mitk::IGTException Throws an exception if get_IDnext failed
*/
int get_IDnext();
/**
* \brief Delete the tool from the list of tools inside API Optitrack
* \return Returns true if the deletion was correct
* @throw mitk::IGTException Throws an exception if
*/
bool DeleteTrackable();
/**
* \brief Set the position to a given one
* @throw mitk::IGTException Throws an exception if
*/
using Superclass::SetPosition;
void SetPosition(mitk::Point3D position, ScalarType eps=0.0);
/**
* \brief Set the orientation to a given one using a quaternion nomenclature
* @throw mitk::IGTException Throws an exception if
*/
using Superclass::SetOrientation;
void SetOrientation(mitk::Quaternion orientation, ScalarType eps=0.0);
/**
* \brief Get the position of the tool
* @throw mitk::IGTException Throws an exception if
*/
void GetPosition(mitk::Point3D& position) const override;
/**
* \brief Get the orientation of the tool using quaternion nomenclature
* @throw mitk::IGTException Throws an exception if
*/
void GetOrientation(mitk::Quaternion& orientation) const override;
/**
* \brief Set the tool enabled for tracking.
* \return Return true if the enabling was successfull
* @throw mitk::IGTException Throws an exception if
*/
bool Enable() override;
/**
* \brief Set the tool disabled for tracking.
* \return Return true if the disabling was successfull
* @throw mitk::IGTException Throws an exception if
*/
bool Disable() override;
/**
* \brief Check if the tool is enabled (true) or not.
* \return Return true if the tool is enabled for tracking
* @throw mitk::IGTException Throws an exception if
*/
bool IsEnabled() const override;
/**
* \brief Check if the data of the tool is valid.
* \return Return true if location data is valid
* @throw mitk::IGTException Throws an exception if
*/
bool IsDataValid() const override;
/**
* \brief Get the expectated error in the tracked tool
* \return Return the error location
* @throw mitk::IGTException Throws an exception if
*/
float GetTrackingError() const override;
/**
* \brief Set the FLE (Fiducial Localization Error) for the tool
* @throw mitk::IGTException Throws an exception if
*/
void SetTrackingError(float FLEerror) override;
/**
* \brief Set the valid flag for tracking data to true
* @throw mitk::IGTException Throws an exception if
*/
void SetDataValid(bool _arg) override;
/**
* \brief Update location and orientation of the tool
* @throw mitk::IGTException Throws an exception if
*/
void updateTool();
/**
* \brief Constructor of the class
*/
OptitrackTrackingTool();
/**
* \brief Destructor of the class
*/
~OptitrackTrackingTool() override;
/**
* \brief File of the configuration for the tool
*/
std::string m_fileConfiguration;
/**
* \brief ID number from Optitrack API
*/
int m_ID;
/**
* \brief List of Markers locations in calibration position and orientation
*/
float* m_calibrationPoints;
/**
* \brief location of the pivot point during calibration
*/
float* m_pivotPoint;
/**
* \brief Number of Markers that blong to the tool
*/
int m_numMarkers;
/**
* \brief Expected value of the fiducial localization error (rms)
*/
float m_FLE;
private:
OptitrackTrackingTool(const OptitrackTrackingTool&);
const OptitrackTrackingTool& operator=(const OptitrackTrackingTool&);
};
}
#endif /* OptiTrackTrackingTool_H_HEADER_INCLUDED_ */
diff --git a/Modules/IGT/TrackingDevices/mitkTrackingDevice.cpp b/Modules/IGT/TrackingDevices/mitkTrackingDevice.cpp
index f468dd7389..7c21f405e7 100644
--- a/Modules/IGT/TrackingDevices/mitkTrackingDevice.cpp
+++ b/Modules/IGT/TrackingDevices/mitkTrackingDevice.cpp
@@ -1,153 +1,146 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkTrackingDevice.h"
#include "mitkIGTTimeStamp.h"
#include "mitkTrackingTool.h"
-#include <itkMutexLockHolder.h>
-
#include <usModuleContext.h>
#include <usGetModuleContext.h>
#include "mitkUnspecifiedTrackingTypeInformation.h"
#include "mitkTrackingDeviceTypeCollection.h"
-typedef itk::MutexLockHolder<itk::FastMutexLock> MutexLockHolder;
-
mitk::TrackingDevice::TrackingDevice() :
m_State(mitk::TrackingDevice::Setup),
m_Data(mitk::UnspecifiedTrackingTypeInformation::GetDeviceDataUnspecified()),
m_StopTracking(false),
m_RotationMode(mitk::TrackingDevice::RotationStandard)
{
- m_StopTrackingMutex = itk::FastMutexLock::New();
- m_StateMutex = itk::FastMutexLock::New();
- m_TrackingFinishedMutex = itk::FastMutexLock::New();
}
mitk::TrackingDevice::~TrackingDevice()
{
}
bool mitk::TrackingDevice::IsDeviceInstalled()
{
return true;
//this is the default for all tracking device
//If a device needs installation please reimplement
//this method in the subclass.
}
bool mitk::TrackingDevice::AutoDetectToolsAvailable()
{
return false;
}
bool mitk::TrackingDevice::AddSingleToolIsAvailable()
{
return true;
}
mitk::NavigationToolStorage::Pointer mitk::TrackingDevice::AutoDetectTools()
{
return mitk::NavigationToolStorage::New();
}
mitk::TrackingDevice::TrackingDeviceState mitk::TrackingDevice::GetState() const
{
- MutexLockHolder lock(*m_StateMutex);
+ std::lock_guard<std::mutex> lock(m_StateMutex);
return m_State;
}
void mitk::TrackingDevice::SetState( TrackingDeviceState state )
{
itkDebugMacro("setting m_State to " << state);
- MutexLockHolder lock(*m_StateMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_StateMutex);
if (m_State == state)
{
return;
}
m_State = state;
this->Modified();
}
void mitk::TrackingDevice::SetRotationMode(RotationMode)
{
MITK_WARN << "Rotation mode switching is not implemented for this device. Leaving it at mitk::TrackingDevice::RotationStandard";
}
mitk::TrackingDeviceType mitk::TrackingDevice::GetType() const{
return m_Data.Line;
}
void mitk::TrackingDevice::SetType(mitk::TrackingDeviceType deviceType){
us::ModuleContext* context = us::GetModuleContext();
std::vector<us::ServiceReference<mitk::TrackingDeviceTypeCollection> > refs = context->GetServiceReferences<mitk::TrackingDeviceTypeCollection>();
if (refs.empty())
{
MITK_ERROR << "No tracking device service found!";
}
mitk::TrackingDeviceTypeCollection* deviceTypeCollection = context->GetService<mitk::TrackingDeviceTypeCollection>(refs.front());
m_Data = deviceTypeCollection->GetFirstCompatibleDeviceDataForLine(deviceType);
}
mitk::TrackingDeviceData mitk::TrackingDevice::GetData() const{
return m_Data;
}
void mitk::TrackingDevice::SetData(mitk::TrackingDeviceData data){
m_Data = data;
}
bool mitk::TrackingDevice::StopTracking()
{
if (this->GetState() == Tracking) // Only if the object is in the correct state
{
- m_StopTrackingMutex->Lock(); // m_StopTracking is used by two threads, so we have to ensure correct thread handling
+ m_StopTrackingMutex.lock(); // m_StopTracking is used by two threads, so we have to ensure correct thread handling
m_StopTracking = true;
- m_StopTrackingMutex->Unlock();
+ m_StopTrackingMutex.unlock();
//we have to wait here that the other thread recognizes the STOP-command and executes it
- m_TrackingFinishedMutex->Lock();
+ m_TrackingFinishedMutex.lock();
mitk::IGTTimeStamp::GetInstance()->Stop(this); // notify realtime clock
// StopTracking was called, thus the mode should be changed back
// to Ready now that the tracking loop has ended.
this->SetState(Ready);
- m_TrackingFinishedMutex->Unlock();
+ m_TrackingFinishedMutex.unlock();
}
return true;
}
mitk::TrackingTool* mitk::TrackingDevice::GetToolByName( std::string name ) const
{
unsigned int toolCount = this->GetToolCount();
for (unsigned int i = 0; i < toolCount; ++i)
if (name == this->GetTool(i)->GetToolName())
return this->GetTool(i);
return nullptr;
}
std::string mitk::TrackingDevice::GetTrackingDeviceName()
{
return this->GetData().Line;
}
diff --git a/Modules/IGT/TrackingDevices/mitkTrackingDevice.h b/Modules/IGT/TrackingDevices/mitkTrackingDevice.h
index d7c49f67bd..4345c50af7 100644
--- a/Modules/IGT/TrackingDevices/mitkTrackingDevice.h
+++ b/Modules/IGT/TrackingDevices/mitkTrackingDevice.h
@@ -1,205 +1,204 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKTRACKINGDEVICE_H_HEADER_INCLUDED_C1C2FCD2
#define MITKTRACKINGDEVICE_H_HEADER_INCLUDED_C1C2FCD2
#include <MitkIGTExports.h>
#include "itkObject.h"
#include "mitkCommon.h"
#include "mitkTrackingTypes.h"
-#include "itkFastMutexLock.h"
#include "mitkNavigationToolStorage.h"
-
+#include <mutex>
namespace mitk {
class TrackingTool; // interface for a tool that can be tracked by the TrackingDevice
/**Documentation
* \brief Interface for all Tracking Devices
*
* Defines the methods that are common for all tracking devices.
*
* \ingroup IGT
*/
class MITKIGT_EXPORT TrackingDevice : public itk::Object
{
public:
mitkClassMacroItkParent(TrackingDevice, itk::Object);
/** Defines the rotation modes of this tracking device which results in different representations
* of quaternions.
*
* - Standard: normal representation, rawdata from the device is not changed (DEFAULT)
*
* - Transposed: the rotation is stored transposed, which is (by mistake!) expected by some older MITK classes due
* to an ambigious method naming in VNL.
*
* CAUTION: The rotation mode can only be changed for backward compatibility of old WRONG code.
* PLEASE DO NOT CHANGE THE ROTATION MODE UNLESS YOU ARE KNOWING EXACTLY WHAT YOU ARE DOING!
*
* use SetRotationMode to change the mode.
*/
enum RotationMode {RotationStandard, RotationTransposed};
enum TrackingDeviceState {Setup, Ready, Tracking}; ///< Type for state variable. The trackingdevice is always in one of these states
/**
* \brief Opens a connection to the device
*
* This may only be called if there is currently no connection to the device.
* If OpenConnection() is successful, the object will change from Setup state to Ready state
*/
virtual bool OpenConnection() = 0;
/**
* \brief Closes the connection to the device
*
* This may only be called if there is currently a connection to the device, but tracking is
* not running (e.g. object is in Ready state)
*/
virtual bool CloseConnection() = 0; ///< Closes the connection with the device
/**
* \brief start retrieving tracking data from the device.
*
* This may only be called after the connection to the device has been established
* with a call to OpenConnection() (E.g. object is in Ready mode). This will change the
* object state from Ready to Tracking
*/
virtual bool StartTracking() = 0;
/**
* \brief stop retrieving tracking data from the device.
* stop retrieving tracking data from the device.
* This may only be called after StartTracking was called
* (e.g. the object is in Tracking mode).
* This will change the object state from Tracking to Ready.
*/
virtual bool StopTracking();
/**
* \brief Return tool with index toolNumber
*
* tools are numbered from 0 to GetToolCount() - 1.
*/
virtual TrackingTool* GetTool(unsigned int toolNumber) const = 0;
/**
* \brief Returns the tool with the given tool name
*
* Note: subclasses can and should implement optimized versions of this method
* \return the given tool or nullptr if no tool with that name exists
*/
virtual mitk::TrackingTool* GetToolByName(std::string name) const;
/**
* \brief Returns number of tracking tools
*/
virtual unsigned int GetToolCount() const = 0;
/** Sets the rotation mode of this class. See documentation of enum RotationMode for details
* on the different modes. This method has to be implemented in a deriving class to become
* functional / if different rotation modes should be supported.
* CAUTION: The rotation mode can only be changed for backward compatibility of old WRONG code.
* PLEASE DO NOT CHANGE THE ROTATION MODE UNLESS YOU ARE KNOWING EXACTLY WHAT YOU ARE DOING!
*/
virtual void SetRotationMode(RotationMode r);
/** @return Returns the rotation mode of this class. See documentation of enum
* RotationMode for details on the different modes.
*/
itkGetConstMacro(RotationMode,RotationMode);
/**
* \brief return current object state (Setup, Ready or Tracking)
*/
TrackingDeviceState GetState() const;
/**
* \brief Deprecated! Use the more specific getData or GetTrackingDeviceName instead. return device type identifier
*/
TrackingDeviceType GetType() const;
/**
* \brief Deprecated! Use the more specific setDeviceData instead. set device type
*/
void SetType(TrackingDeviceType type);
/**
* \brief Convenient Method to get the Name of the Tracking Device.
* This is identical with GetData().Line and can be used to compare with TrackingDeviceTypeInformation::GetTrackingDeviceName()
* to check if you have a specific device.
*/
std::string GetTrackingDeviceName();
/**
* \brief return device data
*/
TrackingDeviceData GetData() const;
/**
* \brief set device type
*/
void SetData(TrackingDeviceData data);
/**
* @return Returns true if the device is installed on this system an can be used.
* Installed means activated in MITK, in some cases this means the MITK
* installation / build has to know the installation path of the device
* libraries on this system. This path is usually given as cmake variable
* during the build configuration in devellopers mode. If the device should
* be available for end users with an installer the libraries can be included
* into the installer or the installer has to be adapted such that it asks
* for the path.
* Returns fals if the device is not installed. It cannot be used on this build
* in this case.
*
* Note that some tracking systems communicate via a standard interface (e.g., serial
* port) and don't need any library or installation. These devices are always "installed".
*/
virtual bool IsDeviceInstalled();
/** @return Returns true if this device can autodetects its tools. */
virtual bool AutoDetectToolsAvailable();
/** @return Returns true if it is possible to add a single tool. Default return is true.*/
virtual bool AddSingleToolIsAvailable();
/** Autodetects tools from this device and returns them as a navigation tool storage.
* @return Returns the detected tools. Returns an empty storage if no tools are present
* or if detection is not possible
*/
virtual mitk::NavigationToolStorage::Pointer AutoDetectTools();
private:
TrackingDeviceState m_State; ///< current object state (Setup, Ready or Tracking)
protected:
/**
* \brief change object state
*/
void SetState(TrackingDeviceState state);
TrackingDevice();
~TrackingDevice() override;
TrackingDeviceData m_Data; ///< current device Data
bool m_StopTracking; ///< signal stop to tracking thread
- itk::FastMutexLock::Pointer m_StopTrackingMutex; ///< mutex to control access to m_StopTracking
- itk::FastMutexLock::Pointer m_TrackingFinishedMutex; ///< mutex to manage control flow of StopTracking()
- itk::FastMutexLock::Pointer m_StateMutex; ///< mutex to control access to m_State
+ std::mutex m_StopTrackingMutex; ///< mutex to control access to m_StopTracking
+ std::mutex m_TrackingFinishedMutex; ///< mutex to manage control flow of StopTracking()
+ mutable std::mutex m_StateMutex; ///< mutex to control access to m_State
RotationMode m_RotationMode; ///< defines the rotation mode Standard or Transposed, Standard is default
};
} // namespace mitk
#endif /* MITKTRACKINGDEVICE_H_HEADER_INCLUDED_C1C2FCD2 */
diff --git a/Modules/IGT/TrackingDevices/mitkTrackingTool.cpp b/Modules/IGT/TrackingDevices/mitkTrackingTool.cpp
index 76ab4f6998..15b30743b3 100644
--- a/Modules/IGT/TrackingDevices/mitkTrackingTool.cpp
+++ b/Modules/IGT/TrackingDevices/mitkTrackingTool.cpp
@@ -1,289 +1,283 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkTrackingTool.h"
-#include <itkMutexLockHolder.h>
-
-typedef itk::MutexLockHolder<itk::FastMutexLock> MutexLockHolder;
mitk::TrackingTool::TrackingTool()
: itk::Object(),
m_ToolName(""),
m_ErrorMessage(""),
m_IGTTimeStamp(0),
- m_MyMutex(itk::FastMutexLock::New()),
m_TrackingError(0.0f),
m_Enabled(true),
m_DataValid(false),
m_ToolTipSet(false)
{
m_Position[0] = 0.0f;
m_Position[1] = 0.0f;
m_Position[2] = 0.0f;
m_Orientation[0] = 0.0f;
m_Orientation[1] = 0.0f;
m_Orientation[2] = 0.0f;
m_Orientation[3] = 0.0f;
// this should not be necessary as the tools bring their own tooltip transformation
m_ToolTipPosition[0] = 0.0f;
m_ToolTipPosition[1] = 0.0f;
m_ToolTipPosition[2] = 0.0f;
m_ToolAxisOrientation[0] = 0.0f;
m_ToolAxisOrientation[1] = 0.0f;
m_ToolAxisOrientation[2] = 0.0f;
m_ToolAxisOrientation[3] = 1.0f;
}
mitk::TrackingTool::~TrackingTool()
{
- m_MyMutex->Unlock();
- m_MyMutex = nullptr;
}
void mitk::TrackingTool::PrintSelf(std::ostream& os, itk::Indent indent) const
{
Superclass::PrintSelf(os, indent);
os << indent << "ToolName: " << m_ToolName << std::endl;
os << indent << "ErrorMesage: " << m_ErrorMessage << std::endl;
os << indent << "Position: " << m_Position << std::endl;
os << indent << "Orientation: " << m_Orientation << std::endl;
os << indent << "TrackingError: " << m_TrackingError << std::endl;
os << indent << "Enabled: " << m_Enabled << std::endl;
os << indent << "DataValid: " << m_DataValid << std::endl;
os << indent << "ToolTip: " << m_ToolTipPosition << std::endl;
os << indent << "ToolTipRotation: " << m_ToolAxisOrientation << std::endl;
os << indent << "ToolTipSet: " << m_ToolTipSet << std::endl;
}
const char* mitk::TrackingTool::GetToolName() const
{
- MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_MyMutex);
return this->m_ToolName.c_str();
}
void mitk::TrackingTool::SetToolName(const char* _arg)
{
itkDebugMacro("setting m_ToolName to " << _arg);
- MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_MyMutex);
if ( _arg && (_arg == this->m_ToolName) )
{
return;
}
if (_arg)
{
this->m_ToolName= _arg;
}
else
{
this->m_ToolName= "";
}
this->Modified();
}
void mitk::TrackingTool::SetToolName( const std::string _arg )
{
this->SetToolName(_arg.c_str());
}
mitk::Point3D mitk::TrackingTool::GetToolTipPosition() const
{
- MutexLockHolder lock(*m_MyMutex);
+ std::lock_guard<std::mutex> lock(m_MyMutex);
return m_ToolTipPosition;
}
mitk::Quaternion mitk::TrackingTool::GetToolAxisOrientation() const
{
- MutexLockHolder lock(*m_MyMutex);
+ std::lock_guard<std::mutex> lock(m_MyMutex);
return m_ToolAxisOrientation;
}
void mitk::TrackingTool::SetToolTipPosition(mitk::Point3D toolTipPosition,
mitk::Quaternion orientation,
mitk::ScalarType eps)
{
if ( !Equal(m_ToolTipPosition, toolTipPosition, eps) ||
!Equal(m_ToolAxisOrientation, orientation, eps) )
{
if( (toolTipPosition[0] == 0) &&
(toolTipPosition[1] == 0) &&
(toolTipPosition[2] == 0) &&
(orientation.x() == 0) &&
(orientation.y() == 0) &&
(orientation.z() == 0) &&
(orientation.r() == 1))
{
m_ToolTipSet = false;
}
else
{
m_ToolTipSet = true;
}
m_ToolTipPosition = toolTipPosition;
m_ToolAxisOrientation = orientation;
this->Modified();
}
}
bool mitk::TrackingTool::IsToolTipSet() const
{
- MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_MyMutex);
return m_ToolTipSet;
}
void mitk::TrackingTool::GetPosition(mitk::Point3D& position) const
{
- MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_MyMutex);
if (m_ToolTipSet)
{
// Compute the position of tool tip in the coordinate frame of the
// tracking device: Rotate the position of the tip into the tracking
// device coordinate frame then add to the position of the tracking
// sensor
vnl_vector<mitk::ScalarType> pos_vnl = m_Position.GetVnlVector() + m_Orientation.rotate( m_ToolTipPosition.GetVnlVector() ) ;
position[0] = pos_vnl[0];
position[1] = pos_vnl[1];
position[2] = pos_vnl[2];
}
else
{
position[0] = m_Position[0];
position[1] = m_Position[1];
position[2] = m_Position[2];
}
this->Modified();
}
void mitk::TrackingTool::SetPosition(mitk::Point3D position)
{
itkDebugMacro("setting m_Position to " << position);
- MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_MyMutex);
if (m_Position != position)
{
m_Position = position;
this->Modified();
}
}
void mitk::TrackingTool::GetOrientation(mitk::Quaternion& orientation) const
{
- MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_MyMutex);
if (m_ToolTipSet)
{
// Compute the orientation of the tool tip in the coordinate frame of
// the tracking device.
//
// * m_Orientation is the orientation of the sensor relative to the transmitter
// * m_ToolAxisOrientation is the orientation of the tool tip relative to the sensor
orientation = m_Orientation * m_ToolAxisOrientation;
}
else
{
orientation = m_Orientation;
}
}
void mitk::TrackingTool::SetOrientation(mitk::Quaternion orientation)
{
itkDebugMacro("setting m_Orientation to " << orientation);
- MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_MyMutex);
if (m_Orientation != orientation)
{
m_Orientation = orientation;
this->Modified();
}
}
bool mitk::TrackingTool::Enable()
{
- MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_MyMutex);
if (m_Enabled == false)
{
this->m_Enabled = true;
this->Modified();
}
return true;
}
bool mitk::TrackingTool::Disable()
{
- MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_MyMutex);
if (m_Enabled == true)
{
this->m_Enabled = false;
this->Modified();
}
return true;
}
bool mitk::TrackingTool::IsEnabled() const
{
- MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_MyMutex);
return m_Enabled;
}
void mitk::TrackingTool::SetDataValid(bool isDataValid)
{
itkDebugMacro("setting m_DataValid to " << isDataValid);
if (this->m_DataValid != isDataValid)
{
- MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_MyMutex);
this->m_DataValid = isDataValid;
this->Modified();
}
}
bool mitk::TrackingTool::IsDataValid() const
{
- MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_MyMutex);
return m_DataValid;
}
float mitk::TrackingTool::GetTrackingError() const
{
- MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_MyMutex);
return m_TrackingError;
}
void mitk::TrackingTool::SetTrackingError(float error)
{
itkDebugMacro("setting m_TrackingError to " << error);
- MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_MyMutex);
if (m_TrackingError != error)
{
m_TrackingError = error;
this->Modified();
}
}
const char* mitk::TrackingTool::GetErrorMessage() const
{
- MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_MyMutex);
return this->m_ErrorMessage.c_str();
}
void mitk::TrackingTool::SetErrorMessage(const char* _arg)
{
itkDebugMacro("setting m_ErrorMessage to " << _arg);
- MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_MyMutex);
if ((_arg == nullptr) || (_arg == this->m_ErrorMessage))
return;
if (_arg != nullptr)
this->m_ErrorMessage = _arg;
else
this->m_ErrorMessage = "";
this->Modified();
}
diff --git a/Modules/IGT/TrackingDevices/mitkTrackingTool.h b/Modules/IGT/TrackingDevices/mitkTrackingTool.h
index f33b54b249..dd02f3ac9d 100644
--- a/Modules/IGT/TrackingDevices/mitkTrackingTool.h
+++ b/Modules/IGT/TrackingDevices/mitkTrackingTool.h
@@ -1,101 +1,101 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKTRACKINGTOOL_H_HEADER_INCLUDED_
#define MITKTRACKINGTOOL_H_HEADER_INCLUDED_
#include <itkObject.h>
#include <MitkIGTExports.h>
#include <mitkCommon.h>
#include <mitkNumericTypes.h>
-#include <itkFastMutexLock.h>
+#include <mutex>
namespace mitk
{
/**Documentation
* \brief Interface for all Tracking Tools
*
* This class is a complete TrackingTool implementation. It can either be used directly by
* TrackingDevices, or be subclassed for more specific implementations.
* mitk::MicroBirdTrackingDevice uses this class to manage its tools. Other tracking devices
* uses specialized versions of this class (e.g. mitk::NDITrackingTool)
*
* The TrackingTool class holds all coordinate transforms associated with tracking of a tool.
* The sensor attached to the tool is localized in the global tracking coordinate system (m_Position, m_Orientation).
* A tool tip (m_ToolTipPosition) can be defined in sensor coordinates.
* The tool axis defines the main axis of the tool and is defined as the negative z-axis of the tool tip coordinate system
* The main axis of the representation object of the tool (e.g. a surface) has to be defined along the negative z-axis
* \imageMacro{TrackingTool.png,"Coordinate transforms associated to the tracking tool.",3}
*
* \ingroup IGT
*/
class MITKIGT_EXPORT TrackingTool : public itk::Object
{
public:
mitkClassMacroItkParent(TrackingTool, itk::Object);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
void PrintSelf(std::ostream& os, itk::Indent indent) const override;
virtual const char* GetToolName() const; ///< every tool has a name thatgit can be used to identify it.
virtual void SetToolName(const std::string _arg); ///< Sets the name of the tool
virtual void SetToolName(const char* _arg); ///< Sets the name of the tool
Point3D GetToolTipPosition() const; ///< returns the tool tip in tool coordinates, which where set by SetToolTip
Quaternion GetToolAxisOrientation() const; ///< returns the transformation of the tool axis with respect to the MITK-IGT main tool axis (0,0,-1)
virtual void SetToolTipPosition(Point3D toolTipPosition, Quaternion orientation, ScalarType eps=0.0); ///< defines a tool tip for this tool in tool coordinates. GetPosition() and GetOrientation() return the data of the tool tip if it is defined. By default no tooltip is defined.
virtual bool IsToolTipSet() const; ///< returns true if a tool tip is set, false if not
virtual void GetPosition(Point3D& position) const; ///< returns the current position of the tool as an array of three floats (in the tracking device coordinate system)
virtual void SetPosition(Point3D position); ///< sets the position
virtual void GetOrientation(Quaternion& orientation) const; ///< returns the current orientation of the tool as a quaternion in a mitk::Point4D (in the tracking device coordinate system)
virtual void SetOrientation(Quaternion orientation); ///< sets the orientation as a quaternion
virtual bool Enable(); ///< enables the tool, so that it will be tracked
virtual bool Disable(); ///< disables the tool, so that it will not be tracked anymore
virtual bool IsEnabled() const; ///< returns whether the tool is enabled or disabled
virtual void SetDataValid(bool isDataValid); ///< sets if the tracking data (position & orientation) is valid
virtual bool IsDataValid() const; ///< returns true if the current position data is valid (no error during tracking, tracking error below threshold, ...)
virtual float GetTrackingError() const; ///< returns one value that corresponds to the overall tracking error.
virtual void SetTrackingError(float error); ///< sets the tracking error
virtual const char* GetErrorMessage() const; ///< if the data is not valid, ErrorMessage should contain a string explaining why it is invalid (the Set-method should be implemented in subclasses, it should not be accessible by the user)
virtual void SetErrorMessage(const char* _arg); ///< sets the error message
itkSetMacro(IGTTimeStamp, double) ///< Sets the IGT timestamp of the tracking tool object (time in milliseconds)
itkGetConstMacro(IGTTimeStamp, double) ///< Gets the IGT timestamp of the tracking tool object (time in milliseconds). Returns 0 if the timestamp was not set.
protected:
TrackingTool();
~TrackingTool() override;
std::string m_ToolName; ///< every tool has a name that can be used to identify it.
std::string m_ErrorMessage; ///< if a tool is invalid, this member should contain a human readable explanation of why it is invalid
double m_IGTTimeStamp; ///< contains the time at which the tracking data was recorded
- itk::FastMutexLock::Pointer m_MyMutex; ///< mutex to control concurrent access to the tool
+ mutable std::mutex m_MyMutex; ///< mutex to control concurrent access to the tool
Point3D m_Position; ///< holds the position of the tool in global tracking coordinates
Quaternion m_Orientation; ///< holds the orientation of the tool´in global tracking coordinates
float m_TrackingError; ///< holds the tracking error of the tool
bool m_Enabled; ///< if true, tool is enabled and should receive tracking updates from the tracking device
bool m_DataValid; ///< if true, data in m_Position and m_Orientation is valid, e.g. true tracking data
Point3D m_ToolTipPosition; ///< holds the position of the tool tip in the coordinate system of the tracking sensor
Quaternion m_ToolAxisOrientation; ///< holds the rotation of the sensor coordinate system such that the z-axis coincides with the main tool axis e.g. obtained by a tool calibration
bool m_ToolTipSet;
};
} // namespace mitk
#endif /* MITKTRACKINGTOOL_H_HEADER_INCLUDED_ */
diff --git a/Modules/IGT/TrackingDevices/mitkVirtualTrackingDevice.cpp b/Modules/IGT/TrackingDevices/mitkVirtualTrackingDevice.cpp
index 51fbec3ea0..cdf8a4a56f 100644
--- a/Modules/IGT/TrackingDevices/mitkVirtualTrackingDevice.cpp
+++ b/Modules/IGT/TrackingDevices/mitkVirtualTrackingDevice.cpp
@@ -1,331 +1,307 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkVirtualTrackingDevice.h"
#include "mitkIGTTimeStamp.h"
#include "mitkIGTException.h"
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <itksys/SystemTools.hxx>
-#include <itkMutexLockHolder.h>
#include <random>
#include <mitkVirtualTrackerTypeInformation.h>
-typedef itk::MutexLockHolder<itk::FastMutexLock> MutexLockHolder;
-
mitk::VirtualTrackingDevice::VirtualTrackingDevice() : mitk::TrackingDevice(),
-m_AllTools(), m_ToolsMutex(nullptr), m_MultiThreader(nullptr), m_ThreadID(-1), m_RefreshRate(100), m_NumberOfControlPoints(20), m_GaussianNoiseEnabled(false),
+m_AllTools(), m_RefreshRate(100), m_NumberOfControlPoints(20), m_GaussianNoiseEnabled(false),
m_MeanDistributionParam(0.0), m_DeviationDistributionParam(1.0)
{
m_Data = mitk::VirtualTrackerTypeInformation::GetDeviceDataVirtualTracker();
m_Bounds[0] = m_Bounds[2] = m_Bounds[4] = -400.0; // initialize bounds to -400 ... +400 (mm) cube
m_Bounds[1] = m_Bounds[3] = m_Bounds[5] = 400.0;
- m_ToolsMutex = itk::FastMutexLock::New();
}
mitk::VirtualTrackingDevice::~VirtualTrackingDevice()
{
if (this->GetState() == Tracking)
{
this->StopTracking();
}
if (this->GetState() == Ready)
{
this->CloseConnection();
}
/* cleanup tracking thread */
- if (m_MultiThreader.IsNotNull() && (m_ThreadID != -1))
- {
- m_MultiThreader->TerminateThread(m_ThreadID);
- m_MultiThreader = nullptr;
- }
+ if (m_Thread.joinable())
+ m_Thread.join();
+
m_AllTools.clear();
}
mitk::TrackingTool* mitk::VirtualTrackingDevice::AddTool(const char* toolName)
{
//if (this->GetState() == Tracking)
//{
// return nullptr;
//}
mitk::VirtualTrackingTool::Pointer t = mitk::VirtualTrackingTool::New();
t->SetToolName(toolName);
t->SetVelocity(0.1);
this->InitializeSpline(t);
- MutexLockHolder lock(*m_ToolsMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_ToolsMutex); // lock and unlock the mutex
m_AllTools.push_back(t);
return t;
}
bool mitk::VirtualTrackingDevice::StartTracking()
{
if (this->GetState() != Ready)
return false;
this->SetState(Tracking); // go to mode Tracking
- this->m_StopTrackingMutex->Lock();
+ this->m_StopTrackingMutex.lock();
this->m_StopTracking = false;
- this->m_StopTrackingMutex->Unlock();
+ this->m_StopTrackingMutex.unlock();
mitk::IGTTimeStamp::GetInstance()->Start(this);
- if (m_MultiThreader.IsNotNull() && (m_ThreadID != -1))
- m_MultiThreader->TerminateThread(m_ThreadID);
- if (m_MultiThreader.IsNull())
- m_MultiThreader = itk::MultiThreader::New();
+ if (m_Thread.joinable())
+ m_Thread.detach();
- m_ThreadID = m_MultiThreader->SpawnThread(this->ThreadStartTracking, this); // start a new thread that executes the TrackTools() method
+ m_Thread = std::thread(&VirtualTrackingDevice::ThreadStartTracking, this); // start a new thread that executes the TrackTools() method
return true;
}
bool mitk::VirtualTrackingDevice::StopTracking()
{
if (this->GetState() == Tracking) // Only if the object is in the correct state
{
- m_StopTrackingMutex->Lock(); // m_StopTracking is used by two threads, so we have to ensure correct thread handling
+ m_StopTrackingMutex.lock(); // m_StopTracking is used by two threads, so we have to ensure correct thread handling
m_StopTracking = true;
- m_StopTrackingMutex->Unlock();
+ m_StopTrackingMutex.unlock();
- m_TrackingFinishedMutex->Lock();
+ m_TrackingFinishedMutex.lock();
this->SetState(Ready);
- m_TrackingFinishedMutex->Unlock();
+ m_TrackingFinishedMutex.unlock();
}
mitk::IGTTimeStamp::GetInstance()->Stop(this);
return true;
}
unsigned int mitk::VirtualTrackingDevice::GetToolCount() const
{
- MutexLockHolder lock(*m_ToolsMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_ToolsMutex); // lock and unlock the mutex
return static_cast<unsigned int>(this->m_AllTools.size());
}
mitk::TrackingTool* mitk::VirtualTrackingDevice::GetTool(unsigned int toolNumber) const
{
- MutexLockHolder lock(*m_ToolsMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> lock(m_ToolsMutex); // lock and unlock the mutex
if (toolNumber < m_AllTools.size())
return this->m_AllTools.at(toolNumber);
return nullptr;
}
bool mitk::VirtualTrackingDevice::OpenConnection()
{
if (m_NumberOfControlPoints < 1)
{
mitkThrowException(mitk::IGTException) << "to few control points for spline interpolation";
}
srand(time(nullptr)); //Init random number generator
this->SetState(Ready);
return true;
}
void mitk::VirtualTrackingDevice::InitializeSpline(mitk::VirtualTrackingTool* t)
{
if (t == nullptr)
return;
typedef mitk::VirtualTrackingTool::SplineType SplineType;
/* create random control points */
SplineType::ControlPointListType controlPoints;
controlPoints.reserve(m_NumberOfControlPoints + 1);
controlPoints.push_back(this->GetRandomPoint()); // insert point 0
double length = 0.0; // estimate spline length by calculating line segments lengths
for (unsigned int i = 1; i < m_NumberOfControlPoints - 1; ++i) // set points 1..n-2
{
SplineType::ControlPointType pos;
pos = this->GetRandomPoint();
length += controlPoints.at(i - 1).EuclideanDistanceTo(pos);
controlPoints.push_back(pos);
}
controlPoints.push_back(controlPoints.at(0)); // close spline --> insert point last control point with same value as first control point
length += controlPoints.at(controlPoints.size() - 2).EuclideanDistanceTo(controlPoints.at(controlPoints.size() - 1));
/* Create knot list. TODO: rethink knot list values and list size. Is there a better solution? */
SplineType::KnotListType knotList;
knotList.push_back(0.0);
for (unsigned int i = 1; i < controlPoints.size() + t->GetSpline()->GetSplineOrder() + 1; ++i)
knotList.push_back(i);
knotList.push_back(controlPoints.size() + t->GetSpline()->GetSplineOrder() + 1);
t->GetSpline()->SetControlPoints(controlPoints);
t->GetSpline()->SetKnots(knotList);
t->SetSplineLength(length);
}
bool mitk::VirtualTrackingDevice::CloseConnection()
{
bool returnValue = true;
if (this->GetState() == Setup)
return true;
this->SetState(Setup);
return returnValue;
}
mitk::ScalarType mitk::VirtualTrackingDevice::GetSplineChordLength(unsigned int idx)
{
mitk::VirtualTrackingTool* t = this->GetInternalTool(idx);
if (t != nullptr)
return t->GetSplineLength();
else
throw std::invalid_argument("invalid index");
}
void mitk::VirtualTrackingDevice::SetToolSpeed(unsigned int idx, mitk::ScalarType roundsPerSecond)
{
if (roundsPerSecond < 0.0001)
throw std::invalid_argument("Minimum tool speed is 0.0001 rounds per second");
mitk::VirtualTrackingTool* t = this->GetInternalTool(idx);
if (t != nullptr)
t->SetVelocity(roundsPerSecond);
else
throw std::invalid_argument("invalid index");
}
mitk::VirtualTrackingTool* mitk::VirtualTrackingDevice::GetInternalTool(unsigned int idx)
{
- MutexLockHolder toolsMutexLockHolder(*m_ToolsMutex); // lock and unlock the mutex
+ std::lock_guard<std::mutex> toolsMutexLockHolder(m_ToolsMutex); // lock and unlock the mutex
if (idx < m_AllTools.size())
return m_AllTools.at(idx);
else
return nullptr;
}
void mitk::VirtualTrackingDevice::TrackTools()
{
/* lock the TrackingFinishedMutex to signal that the execution rights are now transfered to the tracking thread */
- MutexLockHolder trackingFinishedLockHolder(*m_TrackingFinishedMutex); // keep lock until end of scope
+ std::lock_guard<std::mutex> trackingFinishedLockHolder(m_TrackingFinishedMutex); // keep lock until end of scope
if (this->GetState() != Tracking)
return;
bool localStopTracking; // Because m_StopTracking is used by two threads, access has to be guarded by a mutex. To minimize thread locking, a local copy is used here
- this->m_StopTrackingMutex->Lock(); // update the local copy of m_StopTracking
+ this->m_StopTrackingMutex.lock(); // update the local copy of m_StopTracking
localStopTracking = this->m_StopTracking;
- this->m_StopTrackingMutex->Unlock();
+ this->m_StopTrackingMutex.unlock();
mitk::ScalarType t = 0.0;
while ((this->GetState() == Tracking) && (localStopTracking == false))
{
//for (ToolContainer::iterator itAllTools = m_AllTools.begin(); itAllTools != m_AllTools.end(); itAllTools++)
for (unsigned int i = 0; i < this->GetToolCount(); ++i) // use mutexed methods to access tool container
{
mitk::VirtualTrackingTool::Pointer currentTool = this->GetInternalTool(i);
mitk::VirtualTrackingTool::SplineType::PointType pos;
/* calculate tool position with spline interpolation */
pos = currentTool->GetSpline()->EvaluateSpline(t);
mitk::Point3D mp;
mitk::itk2vtk(pos, mp); // convert from SplineType::PointType to mitk::Point3D
//Add Gaussian Noise to Tracking Coordinates if enabled
if (this->m_GaussianNoiseEnabled)
{
std::random_device rd;
std::mt19937 generator(rd());
std::normal_distribution<double> dist(this->m_MeanDistributionParam, this->m_DeviationDistributionParam);
double noise = dist(generator);
mp = mp + noise;
}
currentTool->SetPosition(mp);
// Currently, a constant speed is used. TODO: use tool velocity setting
t += 0.001;
if (t >= 1.0)
t = 0.0;
mitk::Quaternion quat;
/* fix quaternion rotation */
quat.x() = 0.0;
quat.y() = 0.0;
quat.z() = 0.0;
quat.r() = 1.0;
quat.normalize();
currentTool->SetOrientation(quat);
// TODO: rotate once per cycle around a fixed rotation vector
currentTool->SetTrackingError(2 * (rand() / (RAND_MAX + 1.0))); // tracking error in 0 .. 2 Range
currentTool->SetDataValid(true);
currentTool->Modified();
}
itksys::SystemTools::Delay(m_RefreshRate);
/* Update the local copy of m_StopTracking */
- this->m_StopTrackingMutex->Lock();
+ this->m_StopTrackingMutex.lock();
localStopTracking = m_StopTracking;
- this->m_StopTrackingMutex->Unlock();
+ this->m_StopTrackingMutex.unlock();
} // tracking ends if we pass this line
}
-ITK_THREAD_RETURN_TYPE mitk::VirtualTrackingDevice::ThreadStartTracking(void* pInfoStruct)
+void mitk::VirtualTrackingDevice::ThreadStartTracking()
{
- /* extract this pointer from Thread Info structure */
- struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct;
- if (pInfo == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
- if (pInfo->UserData == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
- VirtualTrackingDevice *trackingDevice = static_cast<VirtualTrackingDevice*>(pInfo->UserData);
-
- if (trackingDevice != nullptr)
- trackingDevice->TrackTools();
-
- trackingDevice->m_ThreadID = -1; // reset thread ID because we end the thread here
- return ITK_THREAD_RETURN_VALUE;
+ this->TrackTools();
}
mitk::VirtualTrackingDevice::ControlPointType mitk::VirtualTrackingDevice::GetRandomPoint()
{
ControlPointType pos;
pos[0] = m_Bounds[0] + (m_Bounds[1] - m_Bounds[0]) * (rand() / (RAND_MAX + 1.0)); // X = xMin + xRange * (random number between 0 and 1)
pos[1] = m_Bounds[2] + (m_Bounds[3] - m_Bounds[2]) * (rand() / (RAND_MAX + 1.0)); // Y
pos[2] = m_Bounds[4] + (m_Bounds[5] - m_Bounds[4]) * (rand() / (RAND_MAX + 1.0)); // Z
return pos;
}
void mitk::VirtualTrackingDevice::EnableGaussianNoise()
{
this->m_GaussianNoiseEnabled = true;
}
void mitk::VirtualTrackingDevice::DisableGaussianNoise()
{
this->m_GaussianNoiseEnabled = false;
}
void mitk::VirtualTrackingDevice::SetParamsForGaussianNoise(double meanDistribution, double deviationDistribution)
{
this->m_MeanDistributionParam = meanDistribution;
this->m_DeviationDistributionParam = deviationDistribution;
}
double mitk::VirtualTrackingDevice::GetDeviationDistribution()
{
return m_DeviationDistributionParam;
}
double mitk::VirtualTrackingDevice::GetMeanDistribution()
{
return m_MeanDistributionParam;
}
diff --git a/Modules/IGT/TrackingDevices/mitkVirtualTrackingDevice.h b/Modules/IGT/TrackingDevices/mitkVirtualTrackingDevice.h
index 704f0ca810..253cbe3a12 100644
--- a/Modules/IGT/TrackingDevices/mitkVirtualTrackingDevice.h
+++ b/Modules/IGT/TrackingDevices/mitkVirtualTrackingDevice.h
@@ -1,215 +1,214 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKVIRTUALTRACKINGDEVICE_H_HEADER_INCLUDED_
#define MITKVIRTUALTRACKINGDEVICE_H_HEADER_INCLUDED_
#include <MitkIGTExports.h>
#include <mitkTrackingDevice.h>
#include <mitkVirtualTrackingTool.h>
-#include <itkMultiThreader.h>
-#include "itkFastMutexLock.h"
+#include <mutex>
+#include <thread>
#include <vector>
namespace mitk
{
/** Documentation
* \brief Class representing a tracking device which generates random positions / orientations.
* No hardware is needed for tracking device.
*
* This TrackingDevice class does not interface with a physical tracking device. It simulates
* a tracking device by moving the tools on a randomly generated spline path.
*
* \ingroup IGT
*/
class MITKIGT_EXPORT VirtualTrackingDevice : public TrackingDevice
{
public:
mitkClassMacro(VirtualTrackingDevice, TrackingDevice);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* \brief Sets the refresh rate of the virtual tracking device in ms
* \warning This refresh rate is not guaranteed. A thread is used to refresh the positions
* of the virtual tools. However, this thread may not run at all during this refresh time period.
* \return Sets the refresh rate of the virtual tracking device in ms
*/
itkSetMacro(RefreshRate, unsigned int);
/**
* \brief Returns the refresh rate in ms.
* \return Returns the refresh rate in ms.
*/
itkGetConstMacro(RefreshRate, unsigned int);
/**
* \brief Starts the tracking.
*
* After StartTracking() is called,
* the tools will move on their spline paths with a constant velocity that can be set with
* SetToolSpeed(). The standard velocity is 10 seconds for one complete cycle along the spline path.
* \warning tool speed is not yet used in the current version
* \return Returns true if the tracking is started. Returns false if there was an error.
*/
bool StartTracking() override;
/**
* \brief Stops the tracking.
* \return Returns true if the tracking is stopped. Returns false if there was an error.
*/
bool StopTracking() override;
/**
* \brief Opens the connection to the device. This have to be done before the tracking is started.
* @throw mitk::IGTException Throws an exception if there are two less control points to start the the virtual device.
*/
bool OpenConnection() override;
/**
* \brief Closes the connection and clears all resources.
*/
bool CloseConnection() override;
/**
* \return Returns the number of tools which have been added to the device.
*/
unsigned int GetToolCount() const override;
/**
* \param toolNumber The number of the tool which should be given back.
* \return Returns the tool which the number "toolNumber". Returns nullptr, if there is
* no tool with this number.
*/
TrackingTool* GetTool(unsigned int toolNumber) const override;
/**
* \brief Adds a tool to the tracking device.
*
* The tool will have a random path on which it will move around. The path is created with a
* spline function and random control points inside the tracking volume.
*
* \param toolName The tool which will be added.
* \return Returns true if the tool has been added, false otherwise.
*/
TrackingTool* AddTool(const char* toolName);
/**
* \brief Set the tracking volume bounds
*
* This will set the tracking volume as an axis aligned bounding box
* defined by the six bounds values xMin, xMax, yMin, yMax, zMin, zMax.
* Note that the random path of existing tools will not be updated with the new
* tracking volume. Tools that are created after calling SetBounds() will use the
* new tracking volume
*/
itkSetVectorMacro(Bounds, mitk::ScalarType, 6);
/**
* \brief return the tracking volume bounds
*
* This will return the tracking volume as an axis aligned bounding box
* defined by the six bounds values xMin, xMax, yMin, yMax, zMin, zMax
*/
const mitk::ScalarType* GetBounds() const
{
return m_Bounds;
};
/**
* \brief return the approximate length of the spline for tool with index idx in millimeter
*
* if the index idx is not a
* valid tool index, a std::invalid_argument exception is thrown.
* GetSplineChordLength() returns the distance between all control points of the
* spline in millimeter. This can be used as an approximation for the length of the spline path.
*/
mitk::ScalarType GetSplineChordLength(unsigned int idx);
/**
* \brief sets the speed of the tool idx in rounds per second
*
* The virtual tools will travel along a closed spline path.
* This method sets the speed of a tool as a factor of how many rounds per second
* the tool should move. A setting of 1.0 will indicate one complete round per second.
* Together with GetSplineChordLength(), the speed in millimeter per second can be estimated.
* roundsPerSecond must be positive and larger than 0.0001.
* \warning Tool speed is currently not used.
* \todo use tool speed
*/
void SetToolSpeed(unsigned int idx, mitk::ScalarType roundsPerSecond);
/**
* \brief enable addition of Gaussian Noise to tracking coordinates
*/
void EnableGaussianNoise();
/**
* \brief disable addition of Gaussian Noise to Trackin coordinates
*/
void DisableGaussianNoise();
/**
* \brief sets the mean distribution and the standard deviation for the Gaussian Noise
*
*/
void SetParamsForGaussianNoise(double meanDistribution, double deviationDistribution);
/**
* \brief returns the mean distribution for the Gaussian Noise
*/
double GetMeanDistribution();
/**
* \brief returns the deviation distribution for the Gaussian Noise
*/
double GetDeviationDistribution();
protected:
VirtualTrackingDevice();
~VirtualTrackingDevice() override;
/**
* \brief This method tracks tools as long as the variable m_Mode is set to "Tracking".
* Tracking tools means generating random numbers for the tool position and orientation.
* @throw mitk::IGTException Throws an mitk::IGTException if there is an error during virtual tool tracking.
*/
void TrackTools();
void InitializeSpline(mitk::VirtualTrackingTool* t); ///< initializes the spline path of the tool t with random control points inside the current tracking volume
- static ITK_THREAD_RETURN_TYPE ThreadStartTracking(void* data); ///< static start method for tracking thread
+ void ThreadStartTracking(); ///< static start method for tracking thread
typedef mitk::VirtualTrackingTool::SplineType::ControlPointType ControlPointType;
ControlPointType GetRandomPoint(); ///< returns a random position inside the tracking volume (defined by m_Bounds)
mitk::VirtualTrackingTool* GetInternalTool(unsigned int idx);
typedef std::vector<VirtualTrackingTool::Pointer> ToolContainer; ///< container type for tracking tools
ToolContainer m_AllTools; ///< container for all tracking tools
- itk::FastMutexLock::Pointer m_ToolsMutex; ///< mutex for coordinated access of tool container
+ mutable std::mutex m_ToolsMutex; ///< mutex for coordinated access of tool container
- itk::MultiThreader::Pointer m_MultiThreader; ///< MultiThreader that starts continuous tracking update
- int m_ThreadID;
+ std::thread m_Thread;
unsigned int m_RefreshRate; ///< refresh rate of the internal tracking thread in milliseconds (NOT refreshs per second!)
unsigned int m_NumberOfControlPoints; ///< number of control points for the random path generation
mitk::ScalarType m_Bounds[6]; ///< bounding box of the tracking volume stored as {xMin, xMax, yMin, yMax, zMin, zMax}
bool m_GaussianNoiseEnabled; ///< adding Gaussian Noise to tracking coordinates or not, false by default
double m_MeanDistributionParam; /// mean distribution for Gaussion Noise, 0.0 by default
double m_DeviationDistributionParam; ///< deviation distribution for Gaussian Noise, 1.0 by default
};
}//mitk
#endif /* MITKVIRTUALTRACKINGDEVICE_H_HEADER_INCLUDED_ */
diff --git a/Modules/IGT/TrackingDevices/mitkVirtualTrackingTool.cpp b/Modules/IGT/TrackingDevices/mitkVirtualTrackingTool.cpp
index 659d028447..9c36865587 100644
--- a/Modules/IGT/TrackingDevices/mitkVirtualTrackingTool.cpp
+++ b/Modules/IGT/TrackingDevices/mitkVirtualTrackingTool.cpp
@@ -1,25 +1,22 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkVirtualTrackingTool.h"
-#include <itkMutexLockHolder.h>
-
-typedef itk::MutexLockHolder<itk::FastMutexLock> MutexLockHolder;
mitk::VirtualTrackingTool::VirtualTrackingTool()
: TrackingTool(), m_Spline(SplineType::New()), m_SplineLength(0.0), m_Velocity(0.1)
{
}
mitk::VirtualTrackingTool::~VirtualTrackingTool()
{
}
diff --git a/Modules/IGT/TrackingDevices/mitkVirtualTrackingTool.h b/Modules/IGT/TrackingDevices/mitkVirtualTrackingTool.h
index a908baddba..89fe8fdf77 100644
--- a/Modules/IGT/TrackingDevices/mitkVirtualTrackingTool.h
+++ b/Modules/IGT/TrackingDevices/mitkVirtualTrackingTool.h
@@ -1,63 +1,62 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKVirtualTrackingTool_H_HEADER_INCLUDED_
#define MITKVirtualTrackingTool_H_HEADER_INCLUDED_
#include <mitkTrackingTool.h>
#include <MitkIGTExports.h>
#include <mitkNumericTypes.h>
-#include <itkFastMutexLock.h>
#include <mitkItkNonUniformBSpline.h>
namespace mitk {
/**Documentation
* \brief implements TrackingTool interface
*
* This class is a complete TrackingTool implementation. It can either be used directly by
* TrackingDevices, or be subclassed for more specific implementations.
* mitk::MicroBirdTrackingDevice uses this class to manage its tools. Other tracking devices
* uses specialized versions of this class (e.g. mitk::NDITrackingTool)
*
* \ingroup IGT
*/
class MITKIGT_EXPORT VirtualTrackingTool : public TrackingTool
{
public:
mitkClassMacro(VirtualTrackingTool, TrackingTool);
friend class VirtualTrackingDevice;
itkFactorylessNewMacro(Self)
typedef itk::NonUniformBSpline<3> SplineType; ///< spline type used for tool path interpolation
itkGetMacro(SplineLength, mitk::ScalarType);
itkSetMacro(SplineLength, mitk::ScalarType);
itkGetMacro(Velocity, mitk::ScalarType);
itkSetMacro(Velocity, mitk::ScalarType);
itkGetObjectMacro(Spline, SplineType);
protected:
itkCloneMacro(Self)
VirtualTrackingTool();
~VirtualTrackingTool() override;
SplineType::Pointer m_Spline;
mitk::ScalarType m_SplineLength;
mitk::ScalarType m_Velocity;
};
} // namespace mitk
#endif /* MITKVirtualTrackingTool_H_HEADER_INCLUDED_ */
diff --git a/Modules/ImageDenoising/Testing/itkTotalVariationDenoisingImageFilterTest.cpp b/Modules/ImageDenoising/Testing/itkTotalVariationDenoisingImageFilterTest.cpp
index ac6c9ca227..2b0622a437 100644
--- a/Modules/ImageDenoising/Testing/itkTotalVariationDenoisingImageFilterTest.cpp
+++ b/Modules/ImageDenoising/Testing/itkTotalVariationDenoisingImageFilterTest.cpp
@@ -1,271 +1,277 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "itkImageRegionIterator.h"
#include "itkLocalVariationImageFilter.h"
#include "itkTotalVariationDenoisingImageFilter.h"
#include "itkTotalVariationSingleIterationImageFilter.h"
// image typedefs
typedef itk::Image<float, 3> ImageType;
typedef itk::ImageRegionIterator<ImageType> IteratorType;
// vector image typedefs
typedef itk::Vector<float, 2> VectorPixelType;
typedef itk::Image<VectorPixelType, 3> VectorImageType;
typedef itk::ImageRegionIterator<VectorImageType> VectorIteratorType;
/**
* 3x3x3 test image
*/
ImageType::Pointer GenerateTestImage()
{
// init
ImageType::Pointer image = ImageType::New();
;
// spacing
ImageType::SpacingType spacing;
spacing[0] = 1;
spacing[1] = 1;
spacing[2] = 1;
image->SetSpacing(spacing);
// extent
ImageType::RegionType largestPossibleRegion;
ImageType::SizeType size = {{3, 3, 1}};
largestPossibleRegion.SetSize(size);
ImageType::IndexType index = {{0, 0, 0}};
largestPossibleRegion.SetIndex(index);
image->SetLargestPossibleRegion(largestPossibleRegion);
image->SetBufferedRegion(largestPossibleRegion);
// allocate memory
image->Allocate();
int i = 0;
IteratorType it(image, largestPossibleRegion);
it.GoToBegin();
while (!it.IsAtEnd())
{
it.Set((float)i++);
++it;
}
return image;
}
VectorImageType::Pointer GenerateVectorTestImage()
{
// init
VectorImageType::Pointer image = VectorImageType::New();
;
// spacing
VectorImageType::SpacingType spacing;
spacing[0] = 1;
spacing[1] = 1;
spacing[2] = 1;
image->SetSpacing(spacing);
// extent
VectorImageType::RegionType largestPossibleRegion;
VectorImageType::SizeType size = {{3, 3, 1}};
largestPossibleRegion.SetSize(size);
VectorImageType::IndexType index = {{0, 0, 0}};
largestPossibleRegion.SetIndex(index);
image->SetLargestPossibleRegion(largestPossibleRegion);
image->SetBufferedRegion(largestPossibleRegion);
// allocate memory
image->Allocate();
int i = 0;
VectorIteratorType it(image, largestPossibleRegion);
it.GoToBegin();
while (!it.IsAtEnd())
{
VectorPixelType vec;
vec[0] = (float)i;
vec[1] = (float)i++;
it.Set(vec);
++it;
}
return image;
}
void PrintImage(ImageType::Pointer image)
{
IteratorType it(image, image->GetLargestPossibleRegion());
for (it.GoToBegin(); !it.IsAtEnd(); ++it)
{
std::cout << it.Get() << " ";
}
std::cout << std::endl;
}
void PrintVectorImage(VectorImageType::Pointer image)
{
VectorIteratorType it(image, image->GetLargestPossibleRegion());
for (it.GoToBegin(); !it.IsAtEnd(); ++it)
{
std::cout << it.Get() << " ";
}
std::cout << std::endl;
}
/**
* todo
*/
int itkTotalVariationDenoisingImageFilterTest(int /*argc*/, char * /*argv*/ [])
{
ImageType::Pointer image = GenerateTestImage();
PrintImage(image);
double precision = 0.01;
ImageType::IndexType index = {{1, 1, 0}};
VectorImageType::IndexType vecIndex = {{1, 1, 0}};
try
{
typedef itk::LocalVariationImageFilter<ImageType, ImageType> LocalFilterType;
LocalFilterType::Pointer filter = LocalFilterType::New();
filter->SetInput(image);
- filter->SetNumberOfThreads(1);
+ filter->SetNumberOfWorkUnits(1);
filter->Update();
ImageType::Pointer outImage = filter->GetOutput();
PrintImage(outImage);
if (fabs(outImage->GetPixel(index) - 4.472) > precision)
{
return EXIT_FAILURE;
}
}
- catch (...)
+ catch (const itk::ExceptionObject& e)
{
+ e.Print(std::cerr);
return EXIT_FAILURE;
}
try
{
typedef itk::TotalVariationSingleIterationImageFilter<ImageType, ImageType> SingleFilterType;
SingleFilterType::Pointer sFilter = SingleFilterType::New();
sFilter->SetInput(image);
sFilter->SetOriginalImage(GenerateTestImage());
sFilter->SetLambda(0.5);
- sFilter->SetNumberOfThreads(1);
+ sFilter->SetNumberOfWorkUnits(1);
sFilter->Update();
ImageType::Pointer outImageS = sFilter->GetOutput();
PrintImage(outImageS);
if (fabs(outImageS->GetPixel(index) - 4.0) > precision)
{
return EXIT_FAILURE;
}
}
- catch (...)
+ catch (const itk::ExceptionObject& e)
{
+ e.Print(std::cerr);
return EXIT_FAILURE;
}
try
{
typedef itk::TotalVariationDenoisingImageFilter<ImageType, ImageType> TVFilterType;
TVFilterType::Pointer tvFilter = TVFilterType::New();
tvFilter->SetInput(image);
tvFilter->SetNumberIterations(30);
- tvFilter->SetNumberOfThreads(1);
+ tvFilter->SetNumberOfWorkUnits(1);
tvFilter->SetLambda(0.1);
tvFilter->Update();
ImageType::Pointer outImageTV = tvFilter->GetOutput();
PrintImage(outImageTV);
if (fabs(outImageTV->GetPixel(index) - 4.0) > precision)
{
return EXIT_FAILURE;
}
}
- catch (...)
+ catch (const itk::ExceptionObject& e)
{
+ e.Print(std::cerr);
return EXIT_FAILURE;
}
VectorImageType::Pointer vecImage = GenerateVectorTestImage();
PrintVectorImage(vecImage);
try
{
typedef itk::LocalVariationImageFilter<VectorImageType, ImageType> LocalVecFilterType;
LocalVecFilterType::Pointer vecFilter = LocalVecFilterType::New();
vecFilter->SetInput(vecImage);
- vecFilter->SetNumberOfThreads(1);
+ vecFilter->SetNumberOfWorkUnits(1);
vecFilter->Update();
ImageType::Pointer outVecImage = vecFilter->GetOutput();
PrintImage(outVecImage);
if (fabs(outVecImage->GetPixel(index) - 6.324) > precision)
{
return EXIT_FAILURE;
}
}
- catch (...)
+ catch (const itk::ExceptionObject& e)
{
+ e.Print(std::cerr);
return EXIT_FAILURE;
}
try
{
typedef itk::TotalVariationSingleIterationImageFilter<VectorImageType, VectorImageType> SingleVecFilterType;
SingleVecFilterType::Pointer sVecFilter = SingleVecFilterType::New();
sVecFilter->SetInput(vecImage);
sVecFilter->SetOriginalImage(vecImage);
sVecFilter->SetLambda(0.5);
- sVecFilter->SetNumberOfThreads(1);
+ sVecFilter->SetNumberOfWorkUnits(1);
sVecFilter->UpdateLargestPossibleRegion();
VectorImageType::Pointer outVecImageS = sVecFilter->GetOutput();
PrintVectorImage(outVecImageS);
if (fabs(outVecImageS->GetPixel(vecIndex)[1] - 4.0) > precision)
{
return EXIT_FAILURE;
}
}
- catch (...)
+ catch (const itk::ExceptionObject& e)
{
+ e.Print(std::cerr);
return EXIT_FAILURE;
}
try
{
typedef itk::TotalVariationDenoisingImageFilter<VectorImageType, VectorImageType> TVVectorFilterType;
TVVectorFilterType::Pointer tvVecFilter = TVVectorFilterType::New();
tvVecFilter->SetInput(vecImage);
tvVecFilter->SetNumberIterations(30);
- tvVecFilter->SetNumberOfThreads(1);
+ tvVecFilter->SetNumberOfWorkUnits(1);
tvVecFilter->SetLambda(0.1);
tvVecFilter->Update();
VectorImageType::Pointer outVecImageTV = tvVecFilter->GetOutput();
PrintVectorImage(outVecImageTV);
if (fabs(outVecImageTV->GetPixel(vecIndex)[1] - 4.0) > precision)
{
return EXIT_FAILURE;
}
}
- catch (...)
+ catch (const itk::ExceptionObject& e)
{
+ e.Print(std::cerr);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
diff --git a/Modules/ImageDenoising/itkLocalVariationImageFilter.txx b/Modules/ImageDenoising/itkLocalVariationImageFilter.txx
index 679247d56b..be84c917bb 100644
--- a/Modules/ImageDenoising/itkLocalVariationImageFilter.txx
+++ b/Modules/ImageDenoising/itkLocalVariationImageFilter.txx
@@ -1,188 +1,189 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
/*===================================================================
This file is based heavily on a corresponding ITK filter.
===================================================================*/
#ifndef _itkLocalVariationImageFilter_txx
#define _itkLocalVariationImageFilter_txx
#include "itkLocalVariationImageFilter.h"
#include "itkConstShapedNeighborhoodIterator.h"
#include "itkImageRegionIterator.h"
#include "itkNeighborhoodAlgorithm.h"
#include "itkNeighborhoodInnerProduct.h"
#include "itkOffset.h"
#include "itkProgressReporter.h"
#include "itkVectorImage.h"
#include "itkZeroFluxNeumannBoundaryCondition.h"
#include <algorithm>
#include <vector>
namespace itk
{
template <class TInputImage, class TOutputImage>
LocalVariationImageFilter<TInputImage, TOutputImage>::LocalVariationImageFilter()
{
+ this->DynamicMultiThreadingOff();
}
template <class TInputImage, class TOutputImage>
void LocalVariationImageFilter<TInputImage, TOutputImage>::GenerateInputRequestedRegion()
{
// call the superclass' implementation of this method
Superclass::GenerateInputRequestedRegion();
// get pointers to the input and output
typename Superclass::InputImagePointer inputPtr = const_cast<TInputImage *>(this->GetInput());
typename Superclass::OutputImagePointer outputPtr = this->GetOutput();
if (!inputPtr || !outputPtr)
{
return;
}
// get a copy of the input requested region (should equal the output
// requested region)
typename TInputImage::RegionType inputRequestedRegion;
inputRequestedRegion = inputPtr->GetRequestedRegion();
// pad the input requested region by 1
inputRequestedRegion.PadByRadius(1);
// crop the input requested region at the input's largest possible region
if (inputRequestedRegion.Crop(inputPtr->GetLargestPossibleRegion()))
{
inputPtr->SetRequestedRegion(inputRequestedRegion);
return;
}
else
{
// Couldn't crop the region (requested region is outside the largest
// possible region). Throw an exception.
// store what we tried to request (prior to trying to crop)
inputPtr->SetRequestedRegion(inputRequestedRegion);
// build an exception
InvalidRequestedRegionError e(__FILE__, __LINE__);
e.SetLocation(ITK_LOCATION);
e.SetDescription("Requested region outside possible region.");
e.SetDataObject(inputPtr);
throw e;
}
}
template <>
double SquaredEuclideanMetric<itk::VariableLengthVector<float>>::Calc(itk::VariableLengthVector<float> p)
{
return p.GetSquaredNorm();
}
template <>
double SquaredEuclideanMetric<itk::VariableLengthVector<double>>::Calc(itk::VariableLengthVector<double> p)
{
return p.GetSquaredNorm();
}
template <class TPixelType>
double SquaredEuclideanMetric<TPixelType>::Calc(TPixelType p)
{
return p * p;
}
template <class TInputImage, class TOutputImage>
void LocalVariationImageFilter<TInputImage, TOutputImage>::ThreadedGenerateData(
const OutputImageRegionType &outputRegionForThread, ThreadIdType threadId)
{
// Allocate output
typename OutputImageType::Pointer output = this->GetOutput();
typename InputImageType::ConstPointer input = this->GetInput();
itk::Size<InputImageDimension> size;
for (unsigned int i = 0; i < InputImageDimension; i++)
size[i] = 1;
// Find the data-set boundary "faces"
NeighborhoodAlgorithm::ImageBoundaryFacesCalculator<InputImageType> bC;
typename NeighborhoodAlgorithm::ImageBoundaryFacesCalculator<InputImageType>::FaceListType faceList =
bC(input, outputRegionForThread, size);
// support progress methods/callbacks
ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels());
ZeroFluxNeumannBoundaryCondition<InputImageType> nbc;
std::vector<InputPixelType> pixels;
// Process each of the boundary faces. These are N-d regions which border
// the edge of the buffer.
for (auto fit = faceList.begin(); fit != faceList.end(); ++fit)
{
// iterators over output and input
ImageRegionIterator<OutputImageType> output_image_it(output, *fit);
ImageRegionConstIterator<InputImageType> input_image_it(input.GetPointer(), *fit);
// neighborhood iterator for input image
ConstShapedNeighborhoodIterator<InputImageType> input_image_neighbors_it(size, input, *fit);
typename ConstShapedNeighborhoodIterator<InputImageType>::OffsetType offset;
input_image_neighbors_it.OverrideBoundaryCondition(&nbc);
input_image_neighbors_it.ClearActiveList();
for (unsigned int i = 0; i < InputImageDimension; i++)
{
offset.Fill(0);
offset[i] = -1;
input_image_neighbors_it.ActivateOffset(offset);
offset[i] = 1;
input_image_neighbors_it.ActivateOffset(offset);
}
input_image_neighbors_it.GoToBegin();
// const unsigned int neighborhoodSize = InputImageDimension*2;
while (!input_image_neighbors_it.IsAtEnd())
{
// collect all the pixels in the neighborhood, note that we use
// GetPixel on the NeighborhoodIterator to honor the boundary conditions
typename OutputImageType::PixelType locVariation = 0;
typename ConstShapedNeighborhoodIterator<InputImageType>::ConstIterator input_neighbors_it;
for (input_neighbors_it = input_image_neighbors_it.Begin(); !input_neighbors_it.IsAtEnd(); input_neighbors_it++)
{
typename TInputImage::PixelType diffVec = input_neighbors_it.Get() - input_image_it.Get();
locVariation += SquaredEuclideanMetric<typename TInputImage::PixelType>::Calc(diffVec);
}
locVariation = sqrt(locVariation + 0.0001);
output_image_it.Set(locVariation);
// update iterators
++input_image_neighbors_it;
++output_image_it;
++input_image_it;
// report progress
progress.CompletedPixel();
}
}
}
/**
* Standard "PrintSelf" method
*/
template <class TInputImage, class TOutput>
void LocalVariationImageFilter<TInputImage, TOutput>::PrintSelf(std::ostream &os, Indent indent) const
{
Superclass::PrintSelf(os, indent);
}
} // end namespace itk
#endif //_itkLocalVariationImageFilter_txx
diff --git a/Modules/ImageDenoising/itkTotalVariationDenoisingImageFilter.txx b/Modules/ImageDenoising/itkTotalVariationDenoisingImageFilter.txx
index d5b776752b..25d077d79e 100644
--- a/Modules/ImageDenoising/itkTotalVariationDenoisingImageFilter.txx
+++ b/Modules/ImageDenoising/itkTotalVariationDenoisingImageFilter.txx
@@ -1,105 +1,105 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
/*===================================================================
This file is based heavily on a corresponding ITK filter.
===================================================================*/
#ifndef _itkTotalVariationDenoisingImageFilter_txx
#define _itkTotalVariationDenoisingImageFilter_txx
#include "itkTotalVariationDenoisingImageFilter.h"
#include "itkConstShapedNeighborhoodIterator.h"
#include "itkImageRegionConstIterator.h"
#include "itkImageRegionIterator.h"
#include "itkLocalVariationImageFilter.h"
#include "itkNeighborhoodAlgorithm.h"
#include "itkNeighborhoodInnerProduct.h"
#include "itkOffset.h"
#include "itkProgressReporter.h"
#include "itkZeroFluxNeumannBoundaryCondition.h"
#include <algorithm>
#include <vector>
namespace itk
{
template <class TInputImage, class TOutputImage>
TotalVariationDenoisingImageFilter<TInputImage, TOutputImage>::TotalVariationDenoisingImageFilter()
: m_Lambda(1.0), m_NumberIterations(0)
{
}
template <class TInputImage, class TOutputImage>
void TotalVariationDenoisingImageFilter<TInputImage, TOutputImage>::GenerateData()
{
// first we cast the input image to match output type
typename CastType::Pointer infilter = CastType::New();
infilter->SetInput(this->GetInput());
infilter->Update();
typename TOutputImage::Pointer image = infilter->GetOutput();
// a second copy of the input image is saved as reference
infilter = CastType::New();
infilter->SetInput(this->GetInput());
infilter->Update();
typename TOutputImage::Pointer origImage = infilter->GetOutput();
typename SingleIterationFilterType::Pointer filter;
for (int i = 0; i < m_NumberIterations; i++)
{
filter = SingleIterationFilterType::New();
filter->SetInput(image);
filter->SetOriginalImage(origImage);
filter->SetLambda(m_Lambda);
- filter->SetNumberOfThreads(this->GetNumberOfThreads());
+ filter->SetNumberOfWorkUnits(this->GetNumberOfWorkUnits());
filter->UpdateLargestPossibleRegion();
image = filter->GetOutput();
std::cout << "Iteration " << i + 1 << "/" << m_NumberIterations << std::endl;
}
typename OutputImageType::Pointer output = this->GetOutput();
output->SetSpacing(image->GetSpacing());
typename OutputImageType::RegionType largestPossibleRegion;
largestPossibleRegion.SetSize(image->GetLargestPossibleRegion().GetSize());
largestPossibleRegion.SetIndex(image->GetLargestPossibleRegion().GetIndex());
output->SetLargestPossibleRegion(image->GetLargestPossibleRegion());
output->SetBufferedRegion(image->GetLargestPossibleRegion());
output->Allocate();
itk::ImageRegionIterator<OutputImageType> oit(output, output->GetLargestPossibleRegion());
oit.GoToBegin();
itk::ImageRegionConstIterator<OutputImageType> iit(image, image->GetLargestPossibleRegion());
iit.GoToBegin();
while (!oit.IsAtEnd())
{
oit.Set(iit.Get());
++iit;
++oit;
}
}
/**
* Standard "PrintSelf" method
*/
template <class TInputImage, class TOutput>
void TotalVariationDenoisingImageFilter<TInputImage, TOutput>::PrintSelf(std::ostream &os, Indent indent) const
{
Superclass::PrintSelf(os, indent);
}
} // end namespace itk
#endif
diff --git a/Modules/ImageDenoising/itkTotalVariationSingleIterationImageFilter.txx b/Modules/ImageDenoising/itkTotalVariationSingleIterationImageFilter.txx
index a8fa6f6063..a069c1d957 100644
--- a/Modules/ImageDenoising/itkTotalVariationSingleIterationImageFilter.txx
+++ b/Modules/ImageDenoising/itkTotalVariationSingleIterationImageFilter.txx
@@ -1,251 +1,252 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
/*===================================================================
This file is based heavily on a corresponding ITK filter.
===================================================================*/
#ifndef _itkTotalVariationSingleIterationImageFilter_txx
#define _itkTotalVariationSingleIterationImageFilter_txx
#include "itkTotalVariationSingleIterationImageFilter.h"
// itk includes
#include "itkConstShapedNeighborhoodIterator.h"
#include "itkImageRegionIterator.h"
#include "itkLocalVariationImageFilter.h"
#include "itkNeighborhoodAlgorithm.h"
#include "itkNeighborhoodInnerProduct.h"
#include "itkOffset.h"
#include "itkProgressReporter.h"
#include "itkZeroFluxNeumannBoundaryCondition.h"
// other includes
#include <algorithm>
#include <vector>
namespace itk
{
/**
* constructor
*/
template <class TInputImage, class TOutputImage>
TotalVariationSingleIterationImageFilter<TInputImage, TOutputImage>::TotalVariationSingleIterationImageFilter()
{
+ this->DynamicMultiThreadingOff();
m_Lambda = 1.0;
m_LocalVariation = LocalVariationImageType::New();
}
/**
* generate requested region
*/
template <class TInputImage, class TOutputImage>
void TotalVariationSingleIterationImageFilter<TInputImage, TOutputImage>::GenerateInputRequestedRegion()
{
// call the superclass' implementation of this method
Superclass::GenerateInputRequestedRegion();
// get pointers to the input and output
typename Superclass::InputImagePointer inputPtr = const_cast<TInputImage *>(this->GetInput());
typename Superclass::OutputImagePointer outputPtr = this->GetOutput();
if (!inputPtr || !outputPtr)
{
return;
}
// get a copy of the input requested region (should equal the output
// requested region)
typename TInputImage::RegionType inputRequestedRegion;
inputRequestedRegion = inputPtr->GetRequestedRegion();
// pad the input requested region by 1
inputRequestedRegion.PadByRadius(1);
// crop the input requested region at the input's largest possible region
if (inputRequestedRegion.Crop(inputPtr->GetLargestPossibleRegion()))
{
inputPtr->SetRequestedRegion(inputRequestedRegion);
return;
}
else
{
// Couldn't crop the region (requested region is outside the largest
// possible region). Throw an exception.
// store what we tried to request (prior to trying to crop)
inputPtr->SetRequestedRegion(inputRequestedRegion);
// build an exception
InvalidRequestedRegionError e(__FILE__, __LINE__);
e.SetLocation(ITK_LOCATION);
e.SetDescription("Requested region outside possible region.");
e.SetDataObject(inputPtr);
throw e;
}
}
/**
* generate output
*/
template <class TInputImage, class TOutputImage>
void TotalVariationSingleIterationImageFilter<TInputImage, TOutputImage>::ThreadedGenerateData(
const OutputImageRegionType &outputRegionForThread, ThreadIdType threadId)
{
typename OutputImageType::Pointer output = this->GetOutput();
typename InputImageType::ConstPointer input = this->GetInput();
// Find the data-set boundary "faces"
itk::Size<InputImageDimension> size;
for (unsigned int i = 0; i < InputImageDimension; i++)
size[i] = 1;
NeighborhoodAlgorithm::ImageBoundaryFacesCalculator<InputImageType> bC;
typename NeighborhoodAlgorithm::ImageBoundaryFacesCalculator<InputImageType>::FaceListType faceList =
bC(input, outputRegionForThread, size);
NeighborhoodAlgorithm::ImageBoundaryFacesCalculator<LocalVariationImageType> lv_bC;
typename NeighborhoodAlgorithm::ImageBoundaryFacesCalculator<LocalVariationImageType>::FaceListType lv_faceList =
lv_bC(m_LocalVariation, outputRegionForThread, size);
// support progress methods/callbacks
ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels());
ZeroFluxNeumannBoundaryCondition<InputImageType> nbc;
ZeroFluxNeumannBoundaryCondition<LocalVariationImageType> lv_nbc;
std::vector<double> ws;
std::vector<double> hs;
auto lv_fit = lv_faceList.begin();
// Process each of the boundary faces. These are N-d regions which border
// the edge of the buffer.
for (auto fit = faceList.begin(); fit != faceList.end(); ++fit)
{
// iterators over output, input, original and local variation image
ImageRegionIterator<OutputImageType> output_image_it = ImageRegionIterator<OutputImageType>(output, *fit);
ImageRegionConstIterator<InputImageType> input_image_it = ImageRegionConstIterator<InputImageType>(input, *fit);
ImageRegionConstIterator<InputImageType> orig_image_it =
ImageRegionConstIterator<InputImageType>(m_OriginalImage, *fit);
ImageRegionConstIterator<LocalVariationImageType> loc_var_image_it =
ImageRegionConstIterator<LocalVariationImageType>(m_LocalVariation, *fit);
// neighborhood in input image
ConstShapedNeighborhoodIterator<InputImageType> input_image_neighbors_it(size, input, *fit);
typename ConstShapedNeighborhoodIterator<InputImageType>::OffsetType offset;
input_image_neighbors_it.OverrideBoundaryCondition(&nbc);
input_image_neighbors_it.ClearActiveList();
for (unsigned int i = 0; i < InputImageDimension; i++)
{
offset.Fill(0);
offset[i] = -1;
input_image_neighbors_it.ActivateOffset(offset);
offset[i] = 1;
input_image_neighbors_it.ActivateOffset(offset);
}
input_image_neighbors_it.GoToBegin();
// neighborhood in local variation image
ConstShapedNeighborhoodIterator<LocalVariationImageType> loc_var_image_neighbors_it(
size, m_LocalVariation, *lv_fit);
loc_var_image_neighbors_it.OverrideBoundaryCondition(&lv_nbc);
loc_var_image_neighbors_it.ClearActiveList();
for (unsigned int i = 0; i < InputImageDimension; i++)
{
offset.Fill(0);
offset[i] = -1;
loc_var_image_neighbors_it.ActivateOffset(offset);
offset[i] = 1;
loc_var_image_neighbors_it.ActivateOffset(offset);
}
loc_var_image_neighbors_it.GoToBegin();
const unsigned int neighborhoodSize = InputImageDimension * 2;
ws.resize(neighborhoodSize);
while (!output_image_it.IsAtEnd())
{
// 1 / ||nabla_alpha(u)||_a
double locvar_alpha_inv = 1.0 / loc_var_image_it.Get();
// compute w_alphabetas
int count = 0;
double wsum = 0;
typename ConstShapedNeighborhoodIterator<LocalVariationImageType>::ConstIterator loc_var_neighbors_it;
for (loc_var_neighbors_it = loc_var_image_neighbors_it.Begin(); !loc_var_neighbors_it.IsAtEnd();
loc_var_neighbors_it++)
{
// w_alphabeta(u) =
// 1 / ||nabla_alpha(u)||_a + 1 / ||nabla_beta(u)||_a
ws[count] = locvar_alpha_inv + (1.0 / (double)loc_var_neighbors_it.Get());
wsum += ws[count++];
}
// h_alphaalpha * u_alpha^zero
typename OutputImageType::PixelType res = static_cast<typename OutputImageType::PixelType>(
((typename OutputImageType::PixelType)orig_image_it.Get()) * (m_Lambda / (m_Lambda + wsum)));
// add the different h_alphabeta * u_beta
count = 0;
typename ConstShapedNeighborhoodIterator<InputImageType>::ConstIterator input_neighbors_it;
for (input_neighbors_it = input_image_neighbors_it.Begin(); !input_neighbors_it.IsAtEnd(); input_neighbors_it++)
{
res += input_neighbors_it.Get() * (ws[count++] / (m_Lambda + wsum));
}
// set output result
output_image_it.Set(res);
// increment iterators
++output_image_it;
++input_image_it;
++orig_image_it;
++loc_var_image_it;
++input_image_neighbors_it;
++loc_var_image_neighbors_it;
// report progress
progress.CompletedPixel();
}
++lv_fit;
}
}
/**
* first calculate local variation in the image
*/
template <class TInputImage, class TOutputImage>
void TotalVariationSingleIterationImageFilter<TInputImage, TOutputImage>::BeforeThreadedGenerateData()
{
typedef typename itk::LocalVariationImageFilter<TInputImage, LocalVariationImageType> FilterType;
typename FilterType::Pointer filter = FilterType::New();
filter->SetInput(this->GetInput(0));
- filter->SetNumberOfThreads(this->GetNumberOfThreads());
+ filter->SetNumberOfWorkUnits(this->GetNumberOfWorkUnits());
filter->Update();
this->m_LocalVariation = filter->GetOutput();
}
/**
* Standard "PrintSelf" method
*/
template <class TInputImage, class TOutput>
void TotalVariationSingleIterationImageFilter<TInputImage, TOutput>::PrintSelf(std::ostream &os, Indent indent) const
{
Superclass::PrintSelf(os, indent);
}
} // end namespace itk
#endif
diff --git a/Modules/ImageExtraction/mitkExtractImageFilter.cpp b/Modules/ImageExtraction/mitkExtractImageFilter.cpp
index e23f3f9537..1115c6ebd2 100644
--- a/Modules/ImageExtraction/mitkExtractImageFilter.cpp
+++ b/Modules/ImageExtraction/mitkExtractImageFilter.cpp
@@ -1,259 +1,259 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkExtractImageFilter.h"
#include "mitkITKImageImport.h"
#include "mitkImageCast.h"
#include "mitkImageTimeSelector.h"
#include "mitkPlaneGeometry.h"
#include <itkExtractImageFilter.h>
#include <mitkImageAccessByItk.h>
mitk::ExtractImageFilter::ExtractImageFilter()
: m_SliceIndex(0), m_SliceDimension(0), m_TimeStep(0), m_DirectionCollapseToStrategy(DIRECTIONCOLLAPSETOGUESS)
{
MITK_WARN << "Class ExtractImageFilter is deprecated! Use ExtractSliceFilter instead.";
}
mitk::ExtractImageFilter::~ExtractImageFilter()
{
}
void mitk::ExtractImageFilter::GenerateData()
{
Image::ConstPointer input = ImageToImageFilter::GetInput(0);
if ((input->GetDimension() > 4) || (input->GetDimension() < 2))
{
MITK_ERROR << "mitk::ExtractImageFilter:GenerateData works only with 3D and 3D+t images, sorry." << std::endl;
itkExceptionMacro("mitk::ExtractImageFilter works only with 3D and 3D+t images, sorry.");
return;
}
else if (input->GetDimension() == 4)
{
mitk::ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New();
timeSelector->SetInput(input);
timeSelector->SetTimeNr(m_TimeStep);
timeSelector->UpdateLargestPossibleRegion();
input = timeSelector->GetOutput();
}
else if (input->GetDimension() == 2)
{
Image::Pointer resultImage = ImageToImageFilter::GetOutput();
resultImage = const_cast<Image *>(input.GetPointer());
ImageToImageFilter::SetNthOutput(0, resultImage);
return;
}
if (m_SliceDimension >= input->GetDimension())
{
MITK_ERROR << "mitk::ExtractImageFilter:GenerateData m_SliceDimension == " << m_SliceDimension
<< " makes no sense with an " << input->GetDimension() << "D image." << std::endl;
itkExceptionMacro("This is not a sensible value for m_SliceDimension.");
return;
}
AccessFixedDimensionByItk(input, ItkImageProcessing, 3);
// set a nice geometry for display and point transformations
BaseGeometry *inputImageGeometry = ImageToImageFilter::GetInput(0)->GetGeometry();
if (!inputImageGeometry)
{
MITK_ERROR << "In ExtractImageFilter::ItkImageProcessing: Input image has no geometry!" << std::endl;
return;
}
PlaneGeometry::PlaneOrientation orientation = PlaneGeometry::Axial;
switch (m_SliceDimension)
{
default:
case 2:
orientation = PlaneGeometry::Axial;
break;
case 1:
orientation = PlaneGeometry::Frontal;
break;
case 0:
orientation = PlaneGeometry::Sagittal;
break;
}
PlaneGeometry::Pointer planeGeometry = PlaneGeometry::New();
planeGeometry->InitializeStandardPlane(inputImageGeometry, orientation, (ScalarType)m_SliceIndex, true, false);
Image::Pointer resultImage = ImageToImageFilter::GetOutput();
planeGeometry->ChangeImageGeometryConsideringOriginOffset(true);
resultImage->SetGeometry(planeGeometry);
}
template <typename TPixel, unsigned int VImageDimension>
void mitk::ExtractImageFilter::ItkImageProcessing(const itk::Image<TPixel, VImageDimension> *itkImage)
{
// use the itk::ExtractImageFilter to get a 2D image
typedef itk::Image<TPixel, VImageDimension> ImageType3D;
typedef itk::Image<TPixel, VImageDimension - 1> ImageType2D;
typedef itk::ExtractImageFilter<ImageType3D, ImageType2D> ExtractImageFilterType;
typename ImageType3D::RegionType inSliceRegion = itkImage->GetLargestPossibleRegion();
inSliceRegion.SetSize(m_SliceDimension, 0);
typename ExtractImageFilterType::Pointer sliceExtractor = ExtractImageFilterType::New();
- typename ExtractImageFilterType::DIRECTIONCOLLAPSESTRATEGY collapseStrategy;
+ typename ExtractImageFilterType::DirectionCollapseStrategyEnum collapseStrategy;
switch (m_DirectionCollapseToStrategy)
{
case DIRECTIONCOLLAPSETOUNKOWN:
- collapseStrategy = ExtractImageFilterType::DIRECTIONCOLLAPSETOUNKOWN;
+ collapseStrategy = ExtractImageFilterType::DirectionCollapseStrategyEnum::DIRECTIONCOLLAPSETOUNKOWN;
break;
case DIRECTIONCOLLAPSETOIDENTITY:
- collapseStrategy = ExtractImageFilterType::DIRECTIONCOLLAPSETOIDENTITY;
+ collapseStrategy = ExtractImageFilterType::DirectionCollapseStrategyEnum::DIRECTIONCOLLAPSETOIDENTITY;
break;
case DIRECTIONCOLLAPSETOSUBMATRIX:
- collapseStrategy = ExtractImageFilterType::DIRECTIONCOLLAPSETOSUBMATRIX;
+ collapseStrategy = ExtractImageFilterType::DirectionCollapseStrategyEnum::DIRECTIONCOLLAPSETOSUBMATRIX;
break;
case DIRECTIONCOLLAPSETOGUESS:
default:
- collapseStrategy = ExtractImageFilterType::DIRECTIONCOLLAPSETOGUESS;
+ collapseStrategy = ExtractImageFilterType::DirectionCollapseStrategyEnum::DIRECTIONCOLLAPSETOGUESS;
break;
}
sliceExtractor->SetDirectionCollapseToStrategy(collapseStrategy);
sliceExtractor->SetInput(itkImage);
inSliceRegion.SetIndex(m_SliceDimension, m_SliceIndex);
sliceExtractor->SetExtractionRegion(inSliceRegion);
// calculate the output
sliceExtractor->UpdateLargestPossibleRegion();
typename ImageType2D::Pointer slice = sliceExtractor->GetOutput();
// re-import to MITK
Image::Pointer resultImage = ImageToImageFilter::GetOutput();
GrabItkImageMemory(slice, resultImage, nullptr, false);
}
/*
* What is the input requested region that is required to produce the output
* requested region? By default, the largest possible region is always
* required but this is overridden in many subclasses. For instance, for an
* image processing filter where an output pixel is a simple function of an
* input pixel, the input requested region will be set to the output
* requested region. For an image processing filter where an output pixel is
* a function of the pixels in a neighborhood of an input pixel, then the
* input requested region will need to be larger than the output requested
* region (to avoid introducing artificial boundary conditions). This
* function should never request an input region that is outside the the
* input largest possible region (i.e. implementations of this method should
* crop the input requested region at the boundaries of the input largest
* possible region).
*/
void mitk::ExtractImageFilter::GenerateInputRequestedRegion()
{
Superclass::GenerateInputRequestedRegion();
ImageToImageFilter::InputImagePointer input = dynamic_cast<ImageToImageFilter::InputImageType *>(this->GetInput());
Image::Pointer output = this->GetOutput();
if (input->GetDimension() == 2)
{
input->SetRequestedRegionToLargestPossibleRegion();
return;
}
Image::RegionType requestedRegion;
requestedRegion = output->GetRequestedRegion();
requestedRegion.SetIndex(0, 0);
requestedRegion.SetIndex(1, 0);
requestedRegion.SetIndex(2, 0);
requestedRegion.SetSize(0, input->GetDimension(0));
requestedRegion.SetSize(1, input->GetDimension(1));
requestedRegion.SetSize(2, input->GetDimension(2));
requestedRegion.SetIndex(m_SliceDimension, m_SliceIndex); // only one slice needed
requestedRegion.SetSize(m_SliceDimension, 1);
input->SetRequestedRegion(&requestedRegion);
}
/*
* Generate the information decribing the output data. The default
* implementation of this method will copy information from the input to the
* output. A filter may override this method if its output will have different
* information than its input. For instance, a filter that shrinks an image will
* need to provide an implementation for this method that changes the spacing of
* the pixels. Such filters should call their superclass' implementation of this
* method prior to changing the information values they need (i.e.
* GenerateOutputInformation() should call
* Superclass::GenerateOutputInformation() prior to changing the information.
*/
void mitk::ExtractImageFilter::GenerateOutputInformation()
{
Image::Pointer output = this->GetOutput();
Image::ConstPointer input = this->GetInput();
if (input.IsNull())
return;
if (m_SliceDimension >= input->GetDimension() && input->GetDimension() != 2)
{
MITK_ERROR << "mitk::ExtractImageFilter:GenerateOutputInformation m_SliceDimension == " << m_SliceDimension
<< " makes no sense with an " << input->GetDimension() << "D image." << std::endl;
itkExceptionMacro("This is not a sensible value for m_SliceDimension.");
return;
}
unsigned int sliceDimension(m_SliceDimension);
if (input->GetDimension() == 2)
{
sliceDimension = 2;
}
unsigned int tmpDimensions[2];
switch (sliceDimension)
{
default:
case 2:
// orientation = PlaneGeometry::Axial;
tmpDimensions[0] = input->GetDimension(0);
tmpDimensions[1] = input->GetDimension(1);
break;
case 1:
// orientation = PlaneGeometry::Frontal;
tmpDimensions[0] = input->GetDimension(0);
tmpDimensions[1] = input->GetDimension(2);
break;
case 0:
// orientation = PlaneGeometry::Sagittal;
tmpDimensions[0] = input->GetDimension(1);
tmpDimensions[1] = input->GetDimension(2);
break;
}
output->Initialize(input->GetPixelType(), 2, tmpDimensions, 1 /*input->GetNumberOfChannels()*/);
// initialize the spacing of the output
/*
Vector3D spacing = input->GetSlicedGeometry()->GetSpacing();
if(input->GetDimension()>=2)
spacing[2]=spacing[1];
else
spacing[2] = 1.0;
output->GetSlicedGeometry()->SetSpacing(spacing);
*/
output->SetPropertyList(input->GetPropertyList()->Clone());
}
diff --git a/Modules/ImageStatistics/Testing/mitkImageStatisticsCalculatorTest.cpp b/Modules/ImageStatistics/Testing/mitkImageStatisticsCalculatorTest.cpp
index 164906747a..cfbb184611 100644
--- a/Modules/ImageStatistics/Testing/mitkImageStatisticsCalculatorTest.cpp
+++ b/Modules/ImageStatistics/Testing/mitkImageStatisticsCalculatorTest.cpp
@@ -1,1139 +1,1141 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkImageStatisticsCalculator.h"
#include <mitkTestFixture.h>
#include <mitkTestingMacros.h>
#include <mitkPlanarPolygon.h>
#include <mitkIOUtil.h>
#include <mitkPlanarFigureMaskGenerator.h>
#include <mitkImageMaskGenerator.h>
#include <mitkImageStatisticsConstants.h>
/**
* \brief Test class for mitkImageStatisticsCalculator
*
* This test covers:
* - instantiation of an ImageStatisticsCalculator class
* - correctness of statistics when using PlanarFigures for masking
*/
class mitkImageStatisticsCalculatorTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkImageStatisticsCalculatorTestSuite);
MITK_TEST(TestUninitializedImage);
MITK_TEST(TestCase1);
MITK_TEST(TestCase2);
MITK_TEST(TestCase3);
MITK_TEST(TestCase4);
MITK_TEST(TestCase5);
MITK_TEST(TestCase6);
MITK_TEST(TestCase7);
MITK_TEST(TestCase8);
MITK_TEST(TestCase9);
MITK_TEST(TestCase10);
MITK_TEST(TestCase11);
MITK_TEST(TestCase12);
MITK_TEST(TestPic3DCroppedNoMask);
MITK_TEST(TestPic3DCroppedBinMask);
MITK_TEST(TestPic3DCroppedMultilabelMask);
MITK_TEST(TestPic3DCroppedPlanarFigure);
MITK_TEST(TestUS4DCroppedNoMaskTimeStep1);
MITK_TEST(TestUS4DCroppedBinMaskTimeStep1);
MITK_TEST(TestUS4DCroppedMultilabelMaskTimeStep1);
MITK_TEST(TestUS4DCroppedPlanarFigureTimeStep1);
MITK_TEST(TestUS4DCroppedAllTimesteps);
MITK_TEST(TestUS4DCropped3DMask);
CPPUNIT_TEST_SUITE_END();
public:
void TestUninitializedImage();
void TestCase1();
void TestCase2();
void TestCase3();
void TestCase4();
void TestCase5();
void TestCase6();
void TestCase7();
void TestCase8();
void TestCase9();
void TestCase10();
void TestCase11();
void TestCase12();
void TestPic3DCroppedNoMask();
void TestPic3DCroppedBinMask();
void TestPic3DCroppedMultilabelMask();
void TestPic3DCroppedPlanarFigure();
void TestUS4DCroppedNoMaskTimeStep1();
void TestUS4DCroppedBinMaskTimeStep1();
void TestUS4DCroppedMultilabelMaskTimeStep1();
void TestUS4DCroppedPlanarFigureTimeStep1();
void TestUS4DCroppedAllTimesteps();
void TestUS4DCropped3DMask();
private:
- mitk::Image::ConstPointer m_TestImage;
-
- mitk::Image::ConstPointer m_Pic3DCroppedImage;
- mitk::Image::Pointer m_Pic3DCroppedBinMask;
- mitk::Image::Pointer m_Pic3DCroppedMultilabelMask;
- mitk::PlanarFigure::Pointer m_Pic3DCroppedPlanarFigure;
-
- mitk::Image::ConstPointer m_US4DCroppedImage;
- mitk::Image::Pointer m_US4DCroppedBinMask;
- mitk::Image::Pointer m_US4DCroppedMultilabelMask;
- mitk::Image::Pointer m_US4DCropped3DBinMask;
- mitk::PlanarFigure::Pointer m_US4DCroppedPlanarFigure;
-
- mitk::PlaneGeometry::Pointer m_Geometry;
-
- // creates a polygon given a geometry and a vector of 2d points
- mitk::PlanarPolygon::Pointer GeneratePlanarPolygon(mitk::PlaneGeometry::Pointer geometry, std::vector <mitk::Point2D> points);
-
- // universal function to calculate statistics
- const mitk::ImageStatisticsContainer::Pointer ComputeStatistics(mitk::Image::ConstPointer image,
- mitk::MaskGenerator::Pointer maskGen = nullptr,
- mitk::MaskGenerator::Pointer secondardMaskGen = nullptr,
- unsigned short label = 1);
-
- void VerifyStatistics(mitk::ImageStatisticsContainer::ImageStatisticsObject stats,
- mitk::ImageStatisticsContainer::RealType testMean, mitk::ImageStatisticsContainer::RealType testSD, mitk::ImageStatisticsContainer::RealType testMedian = 0);
-
- // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
- void VerifyStatistics(mitk::ImageStatisticsContainer::ImageStatisticsObject stats,
- mitk::ImageStatisticsContainer::VoxelCountType N,
- mitk::ImageStatisticsContainer::RealType mean,
- mitk::ImageStatisticsContainer::RealType MPP,
- mitk::ImageStatisticsContainer::RealType skewness,
- mitk::ImageStatisticsContainer::RealType kurtosis,
- mitk::ImageStatisticsContainer::RealType variance,
- mitk::ImageStatisticsContainer::RealType stdev,
- mitk::ImageStatisticsContainer::RealType min,
- mitk::ImageStatisticsContainer::RealType max,
- mitk::ImageStatisticsContainer::RealType RMS,
- mitk::ImageStatisticsContainer::IndexType minIndex,
- mitk::ImageStatisticsContainer::IndexType maxIndex);
+ mitk::Image::ConstPointer m_TestImage;
+
+ mitk::Image::ConstPointer m_Pic3DCroppedImage;
+ mitk::Image::Pointer m_Pic3DCroppedBinMask;
+ mitk::Image::Pointer m_Pic3DCroppedMultilabelMask;
+ mitk::PlanarFigure::Pointer m_Pic3DCroppedPlanarFigure;
+
+ mitk::Image::ConstPointer m_US4DCroppedImage;
+ mitk::Image::Pointer m_US4DCroppedBinMask;
+ mitk::Image::Pointer m_US4DCroppedMultilabelMask;
+ mitk::Image::Pointer m_US4DCropped3DBinMask;
+ mitk::PlanarFigure::Pointer m_US4DCroppedPlanarFigure;
+
+ mitk::PlaneGeometry::Pointer m_Geometry;
+
+ // creates a polygon given a geometry and a vector of 2d points
+ mitk::PlanarPolygon::Pointer GeneratePlanarPolygon(mitk::PlaneGeometry::Pointer geometry, std::vector <mitk::Point2D> points);
+
+ // universal function to calculate statistics
+ const mitk::ImageStatisticsContainer::Pointer ComputeStatistics(mitk::Image::ConstPointer image,
+ mitk::MaskGenerator::Pointer maskGen = nullptr,
+ mitk::MaskGenerator::Pointer secondardMaskGen = nullptr,
+ unsigned short label = 1);
+
+ void VerifyStatistics(mitk::ImageStatisticsContainer::ImageStatisticsObject stats,
+ mitk::ImageStatisticsContainer::RealType testMean, mitk::ImageStatisticsContainer::RealType testSD, mitk::ImageStatisticsContainer::RealType testMedian = 0);
+
+ // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
+ void VerifyStatistics(mitk::ImageStatisticsContainer::ImageStatisticsObject stats,
+ mitk::ImageStatisticsContainer::VoxelCountType N,
+ mitk::ImageStatisticsContainer::RealType mean,
+ mitk::ImageStatisticsContainer::RealType MPP,
+ mitk::ImageStatisticsContainer::RealType skewness,
+ mitk::ImageStatisticsContainer::RealType kurtosis,
+ mitk::ImageStatisticsContainer::RealType variance,
+ mitk::ImageStatisticsContainer::RealType stdev,
+ mitk::ImageStatisticsContainer::RealType min,
+ mitk::ImageStatisticsContainer::RealType max,
+ mitk::ImageStatisticsContainer::RealType RMS,
+ mitk::ImageStatisticsContainer::IndexType minIndex,
+ mitk::ImageStatisticsContainer::IndexType maxIndex);
};
void mitkImageStatisticsCalculatorTestSuite::TestUninitializedImage()
{
- /*****************************
- * loading uninitialized image to datastorage
- ******************************/
- MITK_INFO << std::endl << "Test uninitialized image: -----------------------------------------------------------------------------------";
- mitk::Image::Pointer image = mitk::Image::New();
- mitk::DataNode::Pointer node = mitk::DataNode::New();
- node->SetData(image);
-
- mitk::ImageStatisticsCalculator::Pointer is = mitk::ImageStatisticsCalculator::New();
- CPPUNIT_ASSERT_THROW(is->GetStatistics(), mitk::Exception);
+ /*****************************
+ * loading uninitialized image to datastorage
+ ******************************/
+ MITK_INFO << std::endl << "Test uninitialized image: -----------------------------------------------------------------------------------";
+ mitk::Image::Pointer image = mitk::Image::New();
+ mitk::DataNode::Pointer node = mitk::DataNode::New();
+ node->SetData(image);
+
+ mitk::ImageStatisticsCalculator::Pointer is = mitk::ImageStatisticsCalculator::New();
+ CPPUNIT_ASSERT_THROW(is->GetStatistics(), mitk::Exception);
}
void mitkImageStatisticsCalculatorTestSuite::TestCase1()
{
- /*****************************
- * one whole white pixel
- * -> mean of 255 expected
- ******************************/
- MITK_INFO << std::endl << "Test case 1:-----------------------------------------------------------------------------------";
-
- std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
- m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
- CPPUNIT_ASSERT_MESSAGE("Failed loading an mitk::Image", m_TestImage.IsNotNull());
-
- m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
- CPPUNIT_ASSERT_MESSAGE("Failed getting image geometry", m_Geometry.IsNotNull());
-
- mitk::Point2D pnt1; pnt1[0] = 10.5; pnt1[1] = 3.5;
- mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5;
- mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5;
- mitk::Point2D pnt4; pnt4[0] = 10.5; pnt4[1] = 4.5;
- std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
+ /*****************************
+ * one whole white pixel
+ * -> mean of 255 expected
+ ******************************/
+ MITK_INFO << std::endl << "Test case 1:-----------------------------------------------------------------------------------";
+
+ std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
+ m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
+ CPPUNIT_ASSERT_MESSAGE("Failed loading an mitk::Image", m_TestImage.IsNotNull());
+
+ m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
+ CPPUNIT_ASSERT_MESSAGE("Failed getting image geometry", m_Geometry.IsNotNull());
+
+ mitk::Point2D pnt1; pnt1[0] = 10.5; pnt1[1] = 3.5;
+ mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5;
+ mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5;
+ mitk::Point2D pnt4; pnt4[0] = 10.5; pnt4[1] = 4.5;
+ std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
auto figure = GeneratePlanarPolygon(m_Geometry, points);
- mitk::ImageStatisticsContainer::Pointer statisticsContainer;
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer;
- mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
- planFigMaskGen->SetInputImage(m_TestImage);
+ mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
+ planFigMaskGen->SetInputImage(m_TestImage);
planFigMaskGen->SetPlanarFigure(figure.GetPointer());
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
- auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
+ auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
- this->VerifyStatistics(statisticsObjectTimestep0, 255.0, 0.0, 255.0);
+ this->VerifyStatistics(statisticsObjectTimestep0, 255.0, 0.0, 255.0);
}
void mitkImageStatisticsCalculatorTestSuite::TestCase2()
{
- /*****************************
- * half pixel in x-direction (white)
- * -> mean of 255 expected
- ******************************/
- MITK_INFO << std::endl << "Test case 2:-----------------------------------------------------------------------------------";
-
- std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
- m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
- m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
-
- mitk::Point2D pnt1; pnt1[0] = 10.0; pnt1[1] = 3.5;
- mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5;
- mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5;
- mitk::Point2D pnt4; pnt4[0] = 10.0; pnt4[1] = 4.5;
- std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
+ /*****************************
+ * half pixel in x-direction (white)
+ * -> mean of 255 expected
+ ******************************/
+ MITK_INFO << std::endl << "Test case 2:-----------------------------------------------------------------------------------";
+
+ std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
+ m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
+ m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
+
+ mitk::Point2D pnt1; pnt1[0] = 10.0; pnt1[1] = 3.5;
+ mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5;
+ mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5;
+ mitk::Point2D pnt4; pnt4[0] = 10.0; pnt4[1] = 4.5;
+ std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
auto figure = GeneratePlanarPolygon(m_Geometry, points);
- mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
- planFigMaskGen->SetInputImage(m_TestImage);
+ mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
+ planFigMaskGen->SetInputImage(m_TestImage);
planFigMaskGen->SetPlanarFigure(figure.GetPointer());
- mitk::ImageStatisticsContainer::Pointer statisticsContainer;
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
- auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer;
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
+ auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
- this->VerifyStatistics(statisticsObjectTimestep0, 255.0, 0.0, 255.0);
+ this->VerifyStatistics(statisticsObjectTimestep0, 255.0, 0.0, 255.0);
}
void mitkImageStatisticsCalculatorTestSuite::TestCase3()
{
- /*****************************
- * half pixel in diagonal-direction (white)
- * -> mean of 255 expected
- ******************************/
- MITK_INFO << std::endl << "Test case 3:-----------------------------------------------------------------------------------";
-
- std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
- m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
- m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
-
- mitk::Point2D pnt1; pnt1[0] = 10.5; pnt1[1] = 3.5;
- mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5;
- mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5;
- std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3 };
+ /*****************************
+ * half pixel in diagonal-direction (white)
+ * -> mean of 255 expected
+ ******************************/
+ MITK_INFO << std::endl << "Test case 3:-----------------------------------------------------------------------------------";
+
+ std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
+ m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
+ m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
+
+ mitk::Point2D pnt1; pnt1[0] = 10.5; pnt1[1] = 3.5;
+ mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5;
+ mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5;
+ std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3 };
auto figure = GeneratePlanarPolygon(m_Geometry, points);
- mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
- planFigMaskGen->SetInputImage(m_TestImage);
+ mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
+ planFigMaskGen->SetInputImage(m_TestImage);
planFigMaskGen->SetPlanarFigure(figure.GetPointer());
- mitk::ImageStatisticsContainer::Pointer statisticsContainer;
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
- auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer;
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
+ auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
- this->VerifyStatistics(statisticsObjectTimestep0, 255.0, 0.0, 255.0);
+ this->VerifyStatistics(statisticsObjectTimestep0, 255.0, 0.0, 255.0);
}
void mitkImageStatisticsCalculatorTestSuite::TestCase4()
{
- /*****************************
- * one pixel (white) + 2 half pixels (white) + 1 half pixel (black)
- * -> mean of 191.25 expected
- ******************************/
- MITK_INFO << std::endl << "Test case 4:-----------------------------------------------------------------------------------";
-
- std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
- m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
- m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
-
- mitk::Point2D pnt1; pnt1[0] = 1.1; pnt1[1] = 1.1;
- mitk::Point2D pnt2; pnt2[0] = 2.0; pnt2[1] = 2.0;
- mitk::Point2D pnt3; pnt3[0] = 3.0; pnt3[1] = 1.0;
- mitk::Point2D pnt4; pnt4[0] = 2.0; pnt4[1] = 0.0;
- std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
+ /*****************************
+ * one pixel (white) + 2 half pixels (white) + 1 half pixel (black)
+ * -> mean of 191.25 expected
+ ******************************/
+ MITK_INFO << std::endl << "Test case 4:-----------------------------------------------------------------------------------";
+
+ std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
+ m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
+ m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
+
+ mitk::Point2D pnt1; pnt1[0] = 1.1; pnt1[1] = 1.1;
+ mitk::Point2D pnt2; pnt2[0] = 2.0; pnt2[1] = 2.0;
+ mitk::Point2D pnt3; pnt3[0] = 3.0; pnt3[1] = 1.0;
+ mitk::Point2D pnt4; pnt4[0] = 2.0; pnt4[1] = 0.0;
+ std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
auto figure = GeneratePlanarPolygon(m_Geometry, points);
- mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
- planFigMaskGen->SetInputImage(m_TestImage);
+ mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
+ planFigMaskGen->SetInputImage(m_TestImage);
planFigMaskGen->SetPlanarFigure(figure.GetPointer());
- mitk::ImageStatisticsContainer::Pointer statisticsContainer;
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
- auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer;
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
+ auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
- this->VerifyStatistics(statisticsObjectTimestep0, 191.25, 110.41823898251593, 253.72499847412109);
+ this->VerifyStatistics(statisticsObjectTimestep0, 191.25, 127.5, 253.72499847412109);
}
void mitkImageStatisticsCalculatorTestSuite::TestCase5()
{
- /*****************************
- * whole pixel (white) + half pixel (gray) in x-direction
- * -> mean of 191.5 expected
- ******************************/
- MITK_INFO << std::endl << "Test case 5:-----------------------------------------------------------------------------------";
-
- std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
- m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
- m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
-
- mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5;
- mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5;
- mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5;
- mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.5;
- std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
+ /*****************************
+ * whole pixel (white) + half pixel (gray) in x-direction
+ * -> mean of 191.5 expected
+ ******************************/
+ MITK_INFO << std::endl << "Test case 5:-----------------------------------------------------------------------------------";
+
+ std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
+ m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
+ m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
+
+ mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5;
+ mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5;
+ mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5;
+ mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.5;
+ std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
auto figure = GeneratePlanarPolygon(m_Geometry, points);
- mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
- planFigMaskGen->SetInputImage(m_TestImage);
+ mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
+ planFigMaskGen->SetInputImage(m_TestImage);
planFigMaskGen->SetPlanarFigure(figure.GetPointer());
- mitk::ImageStatisticsContainer::Pointer statisticsContainer;
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
- auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer;
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
+ auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
- this->VerifyStatistics(statisticsObjectTimestep0, 191.50, 63.50, 128.63499999046327);
+ this->VerifyStatistics(statisticsObjectTimestep0, 191.50, 89.802561210691536, 128.63499999046327);
}
void mitkImageStatisticsCalculatorTestSuite::TestCase6()
{
- /*****************************
- * quarter pixel (black) + whole pixel (white) + half pixel (gray) in x-direction
- * -> mean of 191.5 expected
- ******************************/
- MITK_INFO << std::endl << "Test case 6:-----------------------------------------------------------------------------------";
-
- std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
- m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
- m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
-
- mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5;
- mitk::Point2D pnt2; pnt2[0] = 9.25; pnt2[1] = 3.5;
- mitk::Point2D pnt3; pnt3[0] = 9.25; pnt3[1] = 4.5;
- mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.5;
- std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
+ /*****************************
+ * quarter pixel (black) + whole pixel (white) + half pixel (gray) in x-direction
+ * -> mean of 191.5 expected
+ ******************************/
+ MITK_INFO << std::endl << "Test case 6:-----------------------------------------------------------------------------------";
+
+ std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
+ m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
+ m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
+
+ mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5;
+ mitk::Point2D pnt2; pnt2[0] = 9.25; pnt2[1] = 3.5;
+ mitk::Point2D pnt3; pnt3[0] = 9.25; pnt3[1] = 4.5;
+ mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.5;
+ std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
auto figure = GeneratePlanarPolygon(m_Geometry, points);
- mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
- planFigMaskGen->SetInputImage(m_TestImage);
+ mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
+ planFigMaskGen->SetInputImage(m_TestImage);
planFigMaskGen->SetPlanarFigure(figure.GetPointer());
- mitk::ImageStatisticsContainer::Pointer statisticsContainer;
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
- auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer;
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
+ auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
- this->VerifyStatistics(statisticsObjectTimestep0, 191.5, 63.50, 128.63499999046327);
+ this->VerifyStatistics(statisticsObjectTimestep0, 191.5, 89.802561210691536, 128.63499999046327);
}
void mitkImageStatisticsCalculatorTestSuite::TestCase7()
{
- /*****************************
- * half pixel (black) + whole pixel (white) + half pixel (gray) in x-direction
- * -> mean of 127.66 expected
- ******************************/
- MITK_INFO << std::endl << "Test case 7:-----------------------------------------------------------------------------------";
-
- std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
- m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
- m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
-
- mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5;
- mitk::Point2D pnt2; pnt2[0] = 9.0; pnt2[1] = 3.5;
- mitk::Point2D pnt3; pnt3[0] = 9.0; pnt3[1] = 4.0;
- mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.0;
- std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
+ /*****************************
+ * half pixel (black) + whole pixel (white) + half pixel (gray) in x-direction
+ * -> mean of 127.66 expected
+ ******************************/
+ MITK_INFO << std::endl << "Test case 7:-----------------------------------------------------------------------------------";
+
+ std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
+ m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
+ m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
+
+ mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5;
+ mitk::Point2D pnt2; pnt2[0] = 9.0; pnt2[1] = 3.5;
+ mitk::Point2D pnt3; pnt3[0] = 9.0; pnt3[1] = 4.0;
+ mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.0;
+ std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
auto figure = GeneratePlanarPolygon(m_Geometry, points);
- mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
- planFigMaskGen->SetInputImage(m_TestImage);
+ mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
+ planFigMaskGen->SetInputImage(m_TestImage);
planFigMaskGen->SetPlanarFigure(figure.GetPointer());
- mitk::ImageStatisticsContainer::Pointer statisticsContainer;
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
- auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer;
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
+ auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
- this->VerifyStatistics(statisticsObjectTimestep0, 127.666666666666667, 104.10358089689113, 128.7750015258789);
+ this->VerifyStatistics(statisticsObjectTimestep0, 127.666666666666667, 127.50032679696680, 128.7750015258789);
}
void mitkImageStatisticsCalculatorTestSuite::TestCase8()
{
- /*****************************
- * whole pixel (gray)
- * -> mean of 128 expected
- ******************************/
- MITK_INFO << std::endl << "Test case 8:-----------------------------------------------------------------------------------";
-
- std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
- m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
- m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
-
- mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5;
- mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 11.5;
- mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 11.5;
- mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5;
- std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
+ /*****************************
+ * whole pixel (gray)
+ * -> mean of 128 expected
+ ******************************/
+ MITK_INFO << std::endl << "Test case 8:-----------------------------------------------------------------------------------";
+
+ std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
+ m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
+ m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
+
+ mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5;
+ mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 11.5;
+ mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 11.5;
+ mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5;
+ std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
auto figure = GeneratePlanarPolygon(m_Geometry, points);
- mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
- planFigMaskGen->SetInputImage(m_TestImage);
+ mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
+ planFigMaskGen->SetInputImage(m_TestImage);
planFigMaskGen->SetPlanarFigure(figure.GetPointer());
- mitk::ImageStatisticsContainer::Pointer statisticsContainer;
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
- auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer;
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
+ auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
- this->VerifyStatistics(statisticsObjectTimestep0, 128.0, 0.0, 128.0);
+ this->VerifyStatistics(statisticsObjectTimestep0, 128.0, 0.0, 128.0);
}
void mitkImageStatisticsCalculatorTestSuite::TestCase9()
{
- /*****************************
- * whole pixel (gray) + half pixel (white) in y-direction
- * -> mean of 191.5 expected
- ******************************/
- MITK_INFO << std::endl << "Test case 9:-----------------------------------------------------------------------------------";
-
- std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
- m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
- m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
-
- mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5;
- mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 12.0;
- mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 12.0;
- mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5;
- std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
+ /*****************************
+ * whole pixel (gray) + half pixel (white) in y-direction
+ * -> mean of 191.5 expected
+ ******************************/
+ MITK_INFO << std::endl << "Test case 9:-----------------------------------------------------------------------------------";
+
+ std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
+ m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
+ m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
+
+ mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5;
+ mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 12.0;
+ mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 12.0;
+ mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5;
+ std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
auto figure = GeneratePlanarPolygon(m_Geometry, points);
- mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
- planFigMaskGen->SetInputImage(m_TestImage);
+ mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
+ planFigMaskGen->SetInputImage(m_TestImage);
planFigMaskGen->SetPlanarFigure(figure.GetPointer());
- mitk::ImageStatisticsContainer::Pointer statisticsContainer;
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
- auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer;
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
+ auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
- this->VerifyStatistics(statisticsObjectTimestep0, 191.5, 63.50, 128.63499999046327);
+ this->VerifyStatistics(statisticsObjectTimestep0, 191.5, 89.802561210691536, 128.63499999046327);
}
void mitkImageStatisticsCalculatorTestSuite::TestCase10()
{
- /*****************************
- * 2 whole pixel (white) + 2 whole pixel (black) in y-direction
- * -> mean of 127.66 expected
- ******************************/
- MITK_INFO << std::endl << "Test case 10:-----------------------------------------------------------------------------------";
-
- std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
- m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
- m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
-
- mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5;
- mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 13.5;
- mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 13.5;
- mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5;
- std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
+ /*****************************
+ * 2 whole pixel (white) + 2 whole pixel (black) in y-direction
+ * -> mean of 127.66 expected
+ ******************************/
+ MITK_INFO << std::endl << "Test case 10:-----------------------------------------------------------------------------------";
+
+ std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
+ m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
+ m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
+
+ mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5;
+ mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 13.5;
+ mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 13.5;
+ mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5;
+ std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
auto figure = GeneratePlanarPolygon(m_Geometry, points);
- mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
- planFigMaskGen->SetInputImage(m_TestImage);
+ mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
+ planFigMaskGen->SetInputImage(m_TestImage);
planFigMaskGen->SetPlanarFigure(figure.GetPointer());
- mitk::ImageStatisticsContainer::Pointer statisticsContainer;
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
- auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer;
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
+ auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
- this->VerifyStatistics(statisticsObjectTimestep0, 127.666666666666667, 104.10358089689113, 128.7750015258789);
+ this->VerifyStatistics(statisticsObjectTimestep0, 127.666666666666667, 127.50032679696680, 128.7750015258789);
}
void mitkImageStatisticsCalculatorTestSuite::TestCase11()
{
- /*****************************
- * 9 whole pixels (white) + 3 half pixels (white)
- * + 3 whole pixel (black) [ + 3 slightly less than half pixels (black)]
- * -> mean of 204.0 expected
- ******************************/
- MITK_INFO << std::endl << "Test case 11:-----------------------------------------------------------------------------------";
- std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
- m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
- m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
-
- mitk::Point2D pnt1; pnt1[0] = 0.5; pnt1[1] = 0.5;
- mitk::Point2D pnt2; pnt2[0] = 3.5; pnt2[1] = 3.5;
- mitk::Point2D pnt3; pnt3[0] = 8.4999; pnt3[1] = 3.5;
- mitk::Point2D pnt4; pnt4[0] = 5.4999; pnt4[1] = 0.5;
- std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
+ /*****************************
+ * 9 whole pixels (white) + 3 half pixels (white)
+ * + 3 whole pixel (black) [ + 3 slightly less than half pixels (black)]
+ * -> mean of 204.0 expected
+ ******************************/
+ MITK_INFO << std::endl << "Test case 11:-----------------------------------------------------------------------------------";
+ std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
+ m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
+ m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
+
+ mitk::Point2D pnt1; pnt1[0] = 0.5; pnt1[1] = 0.5;
+ mitk::Point2D pnt2; pnt2[0] = 3.5; pnt2[1] = 3.5;
+ mitk::Point2D pnt3; pnt3[0] = 8.4999; pnt3[1] = 3.5;
+ mitk::Point2D pnt4; pnt4[0] = 5.4999; pnt4[1] = 0.5;
+ std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3,pnt4 };
auto figure = GeneratePlanarPolygon(m_Geometry, points);
- mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
- planFigMaskGen->SetInputImage(m_TestImage);
+ mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
+ planFigMaskGen->SetInputImage(m_TestImage);
planFigMaskGen->SetPlanarFigure(figure.GetPointer());
- mitk::ImageStatisticsContainer::Pointer statisticsContainer;
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
- auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer;
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
+ auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
- this->VerifyStatistics(statisticsObjectTimestep0, 204.0, 102.00, 253.724998474121083);
+ this->VerifyStatistics(statisticsObjectTimestep0, 204.0, 105.58003057938019, 253.724998474121083);
}
void mitkImageStatisticsCalculatorTestSuite::TestCase12()
{
- /*****************************
- * half pixel (white) + whole pixel (white) + half pixel (black)
- * -> mean of 212.66 expected
- ******************************/
- MITK_INFO << std::endl << "Test case 12:-----------------------------------------------------------------------------------";
-
- std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
- m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
- m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
-
- mitk::Point2D pnt1; pnt1[0] = 9.5; pnt1[1] = 0.5;
- mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 2.5;
- mitk::Point2D pnt3; pnt3[0] = 11.5; pnt3[1] = 2.5;
- std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3 };
+ /*****************************
+ * half pixel (white) + whole pixel (white) + half pixel (black)
+ * -> mean of 212.66 expected
+ ******************************/
+ MITK_INFO << std::endl << "Test case 12:-----------------------------------------------------------------------------------";
+
+ std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd");
+ m_TestImage = mitk::IOUtil::Load<mitk::Image>(filename);
+ m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0);
+
+ mitk::Point2D pnt1; pnt1[0] = 9.5; pnt1[1] = 0.5;
+ mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 2.5;
+ mitk::Point2D pnt3; pnt3[0] = 11.5; pnt3[1] = 2.5;
+ std::vector<mitk::Point2D> points{ pnt1,pnt2,pnt3 };
auto figure = GeneratePlanarPolygon(m_Geometry, points);
- mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
- planFigMaskGen->SetInputImage(m_TestImage);
+ mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New();
+ planFigMaskGen->SetInputImage(m_TestImage);
planFigMaskGen->SetPlanarFigure(figure.GetPointer());
- mitk::ImageStatisticsContainer::Pointer statisticsContainer;
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
- auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer;
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer()));
+ auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
- this->VerifyStatistics(statisticsObjectTimestep0, 212.666666666666667, 59.8683741404609923, 254.36499786376954);
+ this->VerifyStatistics(statisticsObjectTimestep0, 212.666666666666667, 73.323484187082443, 254.36499786376954);
}
// T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
void mitkImageStatisticsCalculatorTestSuite::TestPic3DCroppedNoMask()
{
- MITK_INFO << std::endl << "Test Pic3D cropped without mask:-----------------------------------------------------------------------------------";
-
- std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd");
- m_Pic3DCroppedImage = mitk::IOUtil::Load<mitk::Image>(Pic3DCroppedFile);
- CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull());
- //calculated ground truth via script
- mitk::ImageStatisticsContainer::VoxelCountType expected_N = 27;
- mitk::ImageStatisticsContainer::RealType expected_mean = -564.1481481481481481;
- mitk::ImageStatisticsContainer::RealType expected_MPP = 113.66666666666667;
+ MITK_INFO << std::endl << "Test Pic3D cropped without mask:-----------------------------------------------------------------------------------";
+
+ std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd");
+ m_Pic3DCroppedImage = mitk::IOUtil::Load<mitk::Image>(Pic3DCroppedFile);
+ CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull());
+ //calculated ground truth via script
+ mitk::ImageStatisticsContainer::VoxelCountType expected_N = 27;
+ mitk::ImageStatisticsContainer::RealType expected_mean = -564.1481481481481481;
+ mitk::ImageStatisticsContainer::RealType expected_MPP = 113.66666666666667;
//mitk::ImageStatisticsContainer::RealType expected_median = -825;
- mitk::ImageStatisticsContainer::RealType expected_skewness = 0.7120461106763573;
- mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.8794464383714844;
- mitk::ImageStatisticsContainer::RealType expected_variance = 140541.38545953357;
- mitk::ImageStatisticsContainer::RealType expected_standarddev = 374.88849736892911;
- mitk::ImageStatisticsContainer::RealType expected_min = -927;
- mitk::ImageStatisticsContainer::RealType expected_max = 147;
- mitk::ImageStatisticsContainer::RealType expected_RMS = 677.35110431630551;
- mitk::ImageStatisticsContainer::IndexType expected_minIndex;
- expected_minIndex.set_size(3);
- expected_minIndex[0] = 2;
- expected_minIndex[1] = 1;
- expected_minIndex[2] = 1;
-
- mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
- expected_maxIndex.set_size(3);
- expected_maxIndex[0] = 0;
- expected_maxIndex[1] = 1;
- expected_maxIndex[2] = 2;
-
- mitk::ImageStatisticsContainer::Pointer statisticsContainer;
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage));
- auto statisticsObject = statisticsContainer->GetStatisticsForTimeStep(0);
-
- VerifyStatistics(statisticsObject,
- expected_N,
- expected_mean,
- expected_MPP,
- expected_skewness,
- expected_kurtosis,
- expected_variance,
- expected_standarddev,
- expected_min,
- expected_max,
- expected_RMS,
- expected_minIndex,
- expected_maxIndex);
+ mitk::ImageStatisticsContainer::RealType expected_skewness = 0.7120461106763573;
+ mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.8794464383714844;
+ mitk::ImageStatisticsContainer::RealType expected_variance = 145946.82336182334;
+ mitk::ImageStatisticsContainer::RealType expected_standarddev = 382.02987234223366;
+ mitk::ImageStatisticsContainer::RealType expected_min = -927;
+ mitk::ImageStatisticsContainer::RealType expected_max = 147;
+ mitk::ImageStatisticsContainer::RealType expected_RMS = 681.32955052662169;
+ mitk::ImageStatisticsContainer::IndexType expected_minIndex;
+ expected_minIndex.set_size(3);
+ expected_minIndex[0] = 2;
+ expected_minIndex[1] = 1;
+ expected_minIndex[2] = 1;
+
+ mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
+ expected_maxIndex.set_size(3);
+ expected_maxIndex[0] = 0;
+ expected_maxIndex[1] = 1;
+ expected_maxIndex[2] = 2;
+
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer;
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage));
+ auto statisticsObject = statisticsContainer->GetStatisticsForTimeStep(0);
+
+ VerifyStatistics(statisticsObject,
+ expected_N,
+ expected_mean,
+ expected_MPP,
+ expected_skewness,
+ expected_kurtosis,
+ expected_variance,
+ expected_standarddev,
+ expected_min,
+ expected_max,
+ expected_RMS,
+ expected_minIndex,
+ expected_maxIndex);
}
// T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
void mitkImageStatisticsCalculatorTestSuite::TestPic3DCroppedBinMask()
{
- MITK_INFO << std::endl << "Test Pic3D cropped binary mask:-----------------------------------------------------------------------------------";
-
- std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd");
- m_Pic3DCroppedImage = mitk::IOUtil::Load<mitk::Image>(Pic3DCroppedFile);
- CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull());
-
- std::string Pic3DCroppedBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedBinMask.nrrd");
- m_Pic3DCroppedBinMask = mitk::IOUtil::Load<mitk::Image>(Pic3DCroppedBinMaskFile);
- CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D binary mask", m_Pic3DCroppedBinMask.IsNotNull());
- //calculated ground truth via script
- mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.0765697398089618;
- mitk::ImageStatisticsContainer::RealType expected_MPP = -nan("");
- mitk::ImageStatisticsContainer::RealType expected_max = -22;
- mitk::ImageStatisticsContainer::RealType expected_mean = -464;
- mitk::ImageStatisticsContainer::RealType expected_min = -846;
- mitk::ImageStatisticsContainer::VoxelCountType expected_N = 4;
- mitk::ImageStatisticsContainer::RealType expected_RMS = 595.42631785973322;
- mitk::ImageStatisticsContainer::RealType expected_skewness = 0.0544059290851858;
- mitk::ImageStatisticsContainer::RealType expected_standarddev = 373.14407405183323;
- mitk::ImageStatisticsContainer::RealType expected_variance = 139236.50;
- mitk::ImageStatisticsContainer::IndexType expected_minIndex;
- expected_minIndex.set_size(3);
- expected_minIndex[0] = 1;
- expected_minIndex[1] = 0;
- expected_minIndex[2] = 0;
-
- mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
- expected_maxIndex.set_size(3);
- expected_maxIndex[0] = 0;
- expected_maxIndex[1] = 0;
- expected_maxIndex[2] = 1;
-
- mitk::ImageMaskGenerator::Pointer imgMaskGen = mitk::ImageMaskGenerator::New();
- imgMaskGen->SetImageMask(m_Pic3DCroppedBinMask);
- imgMaskGen->SetInputImage(m_Pic3DCroppedImage);
- imgMaskGen->SetTimeStep(0);
-
- mitk::ImageStatisticsContainer::Pointer statisticsContainer;
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage, imgMaskGen.GetPointer(), nullptr, 1));
- auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
-
- VerifyStatistics(statisticsObjectTimestep0,
- expected_N,
- expected_mean,
- expected_MPP,
- expected_skewness,
- expected_kurtosis,
- expected_variance,
- expected_standarddev,
- expected_min,
- expected_max,
- expected_RMS,
- expected_minIndex,
- expected_maxIndex);
+ MITK_INFO << std::endl << "Test Pic3D cropped binary mask:-----------------------------------------------------------------------------------";
+
+ std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd");
+ m_Pic3DCroppedImage = mitk::IOUtil::Load<mitk::Image>(Pic3DCroppedFile);
+ CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull());
+
+ std::string Pic3DCroppedBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedBinMask.nrrd");
+ m_Pic3DCroppedBinMask = mitk::IOUtil::Load<mitk::Image>(Pic3DCroppedBinMaskFile);
+ CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D binary mask", m_Pic3DCroppedBinMask.IsNotNull());
+ //calculated ground truth via script
+ mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.0765697398089618;
+ mitk::ImageStatisticsContainer::RealType expected_MPP = -nan("");
+ mitk::ImageStatisticsContainer::RealType expected_max = -22;
+ mitk::ImageStatisticsContainer::RealType expected_mean = -464;
+ mitk::ImageStatisticsContainer::RealType expected_min = -846;
+ mitk::ImageStatisticsContainer::VoxelCountType expected_N = 4;
+ mitk::ImageStatisticsContainer::RealType expected_RMS = 633.20191618998331;
+ mitk::ImageStatisticsContainer::RealType expected_skewness = 0.0544059290851858;
+ mitk::ImageStatisticsContainer::RealType expected_standarddev = 430.86966320067910;
+ mitk::ImageStatisticsContainer::RealType expected_variance = 185648.66666666663;
+ mitk::ImageStatisticsContainer::IndexType expected_minIndex;
+ expected_minIndex.set_size(3);
+ expected_minIndex[0] = 1;
+ expected_minIndex[1] = 0;
+ expected_minIndex[2] = 0;
+
+ mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
+ expected_maxIndex.set_size(3);
+ expected_maxIndex[0] = 0;
+ expected_maxIndex[1] = 0;
+ expected_maxIndex[2] = 1;
+
+ mitk::ImageMaskGenerator::Pointer imgMaskGen = mitk::ImageMaskGenerator::New();
+ imgMaskGen->SetImageMask(m_Pic3DCroppedBinMask);
+ imgMaskGen->SetInputImage(m_Pic3DCroppedImage);
+ imgMaskGen->SetTimeStep(0);
+
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer;
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage, imgMaskGen.GetPointer(), nullptr, 1));
+ auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
+
+ VerifyStatistics(statisticsObjectTimestep0,
+ expected_N,
+ expected_mean,
+ expected_MPP,
+ expected_skewness,
+ expected_kurtosis,
+ expected_variance,
+ expected_standarddev,
+ expected_min,
+ expected_max,
+ expected_RMS,
+ expected_minIndex,
+ expected_maxIndex);
}
// T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
void mitkImageStatisticsCalculatorTestSuite::TestPic3DCroppedMultilabelMask()
{
- MITK_INFO << std::endl << "Test Pic3D cropped multilabel mask:-----------------------------------------------------------------------------------";
-
- std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd");
- m_Pic3DCroppedImage = mitk::IOUtil::Load<mitk::Image>(Pic3DCroppedFile);
- CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull());
-
- std::string Pic3DCroppedMultilabelMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedMultilabelMask.nrrd");
- m_Pic3DCroppedMultilabelMask = mitk::IOUtil::Load<mitk::Image>(Pic3DCroppedMultilabelMaskFile);
- CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D multilabel mask", m_Pic3DCroppedMultilabelMask.IsNotNull());
- //calculated ground truth via script
- mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.5;
- mitk::ImageStatisticsContainer::RealType expected_MPP = -nan("");
- mitk::ImageStatisticsContainer::RealType expected_max = -22;
- mitk::ImageStatisticsContainer::RealType expected_mean = -586.33333333333333;
- mitk::ImageStatisticsContainer::RealType expected_min = -916;
- mitk::ImageStatisticsContainer::VoxelCountType expected_N = 3;
- mitk::ImageStatisticsContainer::RealType expected_RMS = 710.3006405741163;
- mitk::ImageStatisticsContainer::RealType expected_skewness = 0.6774469597523700;
- mitk::ImageStatisticsContainer::RealType expected_standarddev = 400.92421007245525;
- mitk::ImageStatisticsContainer::RealType expected_variance = 160740.22222222222;
- mitk::ImageStatisticsContainer::IndexType expected_minIndex;
- expected_minIndex.set_size(3);
- expected_minIndex[0] = 2;
- expected_minIndex[1] = 0;
- expected_minIndex[2] = 1;
-
- mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
- expected_maxIndex.set_size(3);
- expected_maxIndex[0] = 0;
- expected_maxIndex[1] = 0;
- expected_maxIndex[2] = 1;
-
- mitk::ImageMaskGenerator::Pointer imgMaskGen = mitk::ImageMaskGenerator::New();
- imgMaskGen->SetImageMask(m_Pic3DCroppedMultilabelMask);
- imgMaskGen->SetInputImage(m_Pic3DCroppedImage);
- imgMaskGen->SetTimeStep(0);
-
- mitk::ImageStatisticsContainer::Pointer statisticsContainer;
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage, imgMaskGen.GetPointer(), nullptr, 2));
- auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
-
- VerifyStatistics(statisticsObjectTimestep0,
- expected_N,
- expected_mean,
- expected_MPP,
- expected_skewness,
- expected_kurtosis,
- expected_variance,
- expected_standarddev,
- expected_min,
- expected_max,
- expected_RMS,
- expected_minIndex,
- expected_maxIndex);
+ MITK_INFO << std::endl << "Test Pic3D cropped multilabel mask:-----------------------------------------------------------------------------------";
+
+ std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd");
+ m_Pic3DCroppedImage = mitk::IOUtil::Load<mitk::Image>(Pic3DCroppedFile);
+ CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull());
+
+ std::string Pic3DCroppedMultilabelMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedMultilabelMask.nrrd");
+ m_Pic3DCroppedMultilabelMask = mitk::IOUtil::Load<mitk::Image>(Pic3DCroppedMultilabelMaskFile);
+ CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D multilabel mask", m_Pic3DCroppedMultilabelMask.IsNotNull());
+ //calculated ground truth via script
+ mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.5;
+ mitk::ImageStatisticsContainer::RealType expected_MPP = -nan("");
+ mitk::ImageStatisticsContainer::RealType expected_max = -22;
+ mitk::ImageStatisticsContainer::RealType expected_mean = -586.33333333333333;
+ mitk::ImageStatisticsContainer::RealType expected_min = -916;
+ mitk::ImageStatisticsContainer::VoxelCountType expected_N = 3;
+ mitk::ImageStatisticsContainer::RealType expected_RMS = 764.78566351044469;
+ mitk::ImageStatisticsContainer::RealType expected_skewness = 0.6774469597523700;
+ mitk::ImageStatisticsContainer::RealType expected_standarddev = 491.02987010296363;
+ mitk::ImageStatisticsContainer::RealType expected_variance = 241110.33333333334;
+ mitk::ImageStatisticsContainer::IndexType expected_minIndex;
+ expected_minIndex.set_size(3);
+ expected_minIndex[0] = 2;
+ expected_minIndex[1] = 0;
+ expected_minIndex[2] = 1;
+
+ mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
+ expected_maxIndex.set_size(3);
+ expected_maxIndex[0] = 0;
+ expected_maxIndex[1] = 0;
+ expected_maxIndex[2] = 1;
+
+ mitk::ImageMaskGenerator::Pointer imgMaskGen = mitk::ImageMaskGenerator::New();
+ imgMaskGen->SetImageMask(m_Pic3DCroppedMultilabelMask);
+ imgMaskGen->SetInputImage(m_Pic3DCroppedImage);
+ imgMaskGen->SetTimeStep(0);
+
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer;
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage, imgMaskGen.GetPointer(), nullptr, 2));
+ auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
+
+ VerifyStatistics(statisticsObjectTimestep0,
+ expected_N,
+ expected_mean,
+ expected_MPP,
+ expected_skewness,
+ expected_kurtosis,
+ expected_variance,
+ expected_standarddev,
+ expected_min,
+ expected_max,
+ expected_RMS,
+ expected_minIndex,
+ expected_maxIndex);
}
// T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
void mitkImageStatisticsCalculatorTestSuite::TestPic3DCroppedPlanarFigure()
{
- MITK_INFO << std::endl << "Test Pic3D cropped planar figure:-----------------------------------------------------------------------------------";
-
- std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd");
- m_Pic3DCroppedImage = mitk::IOUtil::Load<mitk::Image>(Pic3DCroppedFile);
- CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull());
-
- std::string Pic3DCroppedPlanarFigureFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedPF.pf");
- m_Pic3DCroppedPlanarFigure = mitk::IOUtil::Load<mitk::PlanarFigure>(Pic3DCroppedPlanarFigureFile);
- CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D planar figure", m_Pic3DCroppedPlanarFigure.IsNotNull());
- //calculated ground truth via script
- mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1;
- mitk::ImageStatisticsContainer::RealType expected_MPP = -nan("");
- mitk::ImageStatisticsContainer::RealType expected_max = -67;
- mitk::ImageStatisticsContainer::RealType expected_mean = -446;
- mitk::ImageStatisticsContainer::RealType expected_min = -825;
- mitk::ImageStatisticsContainer::VoxelCountType expected_N = 2;
- mitk::ImageStatisticsContainer::RealType expected_RMS = 585.28369189650243;
- mitk::ImageStatisticsContainer::RealType expected_skewness = 0;
- mitk::ImageStatisticsContainer::RealType expected_standarddev = 379;
- mitk::ImageStatisticsContainer::RealType expected_variance = 143641;
- mitk::ImageStatisticsContainer::IndexType expected_minIndex;
- expected_minIndex.set_size(3);
- expected_minIndex[0] = 1;
- expected_minIndex[1] = 1;
- expected_minIndex[2] = 1;
-
- mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
- expected_maxIndex.set_size(3);
- expected_maxIndex[0] = 0;
- expected_maxIndex[1] = 1;
- expected_maxIndex[2] = 1;
-
- mitk::PlanarFigureMaskGenerator::Pointer pfMaskGen = mitk::PlanarFigureMaskGenerator::New();
- pfMaskGen->SetInputImage(m_Pic3DCroppedImage);
- pfMaskGen->SetPlanarFigure(m_Pic3DCroppedPlanarFigure);
-
- mitk::ImageStatisticsContainer::Pointer statisticsContainer;
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage, pfMaskGen.GetPointer()));
- auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
-
- VerifyStatistics(statisticsObjectTimestep0,
- expected_N,
- expected_mean,
- expected_MPP,
- expected_skewness,
- expected_kurtosis,
- expected_variance,
- expected_standarddev,
- expected_min,
- expected_max,
- expected_RMS,
- expected_minIndex,
- expected_maxIndex);
+ MITK_INFO << std::endl << "Test Pic3D cropped planar figure:-----------------------------------------------------------------------------------";
+
+ std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd");
+ m_Pic3DCroppedImage = mitk::IOUtil::Load<mitk::Image>(Pic3DCroppedFile);
+ CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull());
+
+ std::string Pic3DCroppedPlanarFigureFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedPF.pf");
+ m_Pic3DCroppedPlanarFigure = mitk::IOUtil::Load<mitk::PlanarFigure>(Pic3DCroppedPlanarFigureFile);
+ CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D planar figure", m_Pic3DCroppedPlanarFigure.IsNotNull());
+ //calculated ground truth via script
+ mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1;
+ mitk::ImageStatisticsContainer::RealType expected_MPP = -nan("");
+ mitk::ImageStatisticsContainer::RealType expected_max = -67;
+ mitk::ImageStatisticsContainer::RealType expected_mean = -446;
+ mitk::ImageStatisticsContainer::RealType expected_min = -825;
+ mitk::ImageStatisticsContainer::VoxelCountType expected_N = 2;
+ mitk::ImageStatisticsContainer::RealType expected_RMS = 697.27899724572228;
+ mitk::ImageStatisticsContainer::RealType expected_skewness = 0;
+ mitk::ImageStatisticsContainer::RealType expected_standarddev = 535.98694013940303;
+ mitk::ImageStatisticsContainer::RealType expected_variance = 287282.0;
+ mitk::ImageStatisticsContainer::IndexType expected_minIndex;
+ expected_minIndex.set_size(3);
+ expected_minIndex[0] = 1;
+ expected_minIndex[1] = 1;
+ expected_minIndex[2] = 1;
+
+ mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
+ expected_maxIndex.set_size(3);
+ expected_maxIndex[0] = 0;
+ expected_maxIndex[1] = 1;
+ expected_maxIndex[2] = 1;
+
+ mitk::PlanarFigureMaskGenerator::Pointer pfMaskGen = mitk::PlanarFigureMaskGenerator::New();
+ pfMaskGen->SetInputImage(m_Pic3DCroppedImage);
+ pfMaskGen->SetPlanarFigure(m_Pic3DCroppedPlanarFigure);
+
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer;
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage, pfMaskGen.GetPointer()));
+ auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0);
+
+ VerifyStatistics(statisticsObjectTimestep0,
+ expected_N,
+ expected_mean,
+ expected_MPP,
+ expected_skewness,
+ expected_kurtosis,
+ expected_variance,
+ expected_standarddev,
+ expected_min,
+ expected_max,
+ expected_RMS,
+ expected_minIndex,
+ expected_maxIndex);
}
// T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
void mitkImageStatisticsCalculatorTestSuite::TestUS4DCroppedNoMaskTimeStep1()
{
- MITK_INFO << std::endl << "Test US4D cropped without mask timestep 1:-----------------------------------------------------------------------------------";
+ MITK_INFO << std::endl << "Test US4D cropped without mask timestep 1:-----------------------------------------------------------------------------------";
std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd");
- m_US4DCroppedImage = mitk::IOUtil::Load<mitk::Image>(US4DCroppedFile);
- CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull());
- //calculated ground truth via script
- mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.5398359155908228;
- mitk::ImageStatisticsContainer::RealType expected_MPP = 157.74074074074073;
- mitk::ImageStatisticsContainer::RealType expected_max = 199;
- mitk::ImageStatisticsContainer::RealType expected_mean = 157.74074074074073;
- mitk::ImageStatisticsContainer::RealType expected_min = 101;
- mitk::ImageStatisticsContainer::VoxelCountType expected_N = 27;
- mitk::ImageStatisticsContainer::RealType expected_RMS = 160.991718213494823;
- mitk::ImageStatisticsContainer::RealType expected_skewness = 0.0347280313508018;
- mitk::ImageStatisticsContainer::RealType expected_standarddev = 32.189936997387058;
- mitk::ImageStatisticsContainer::RealType expected_variance = 1036.19204389574722;
- mitk::ImageStatisticsContainer::IndexType expected_minIndex;
- expected_minIndex.set_size(3);
- expected_minIndex[0] = 0;
- expected_minIndex[1] = 2;
- expected_minIndex[2] = 0;
-
- mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
- expected_maxIndex.set_size(3);
- expected_maxIndex[0] = 0;
- expected_maxIndex[1] = 0;
- expected_maxIndex[2] = 1;
-
- mitk::ImageStatisticsContainer::Pointer statisticsContainer=mitk::ImageStatisticsContainer::New();
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage));
- auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1);
-
- VerifyStatistics(statisticsObjectTimestep1,
- expected_N,
- expected_mean,
- expected_MPP,
- expected_skewness,
- expected_kurtosis,
- expected_variance,
- expected_standarddev,
- expected_min,
- expected_max,
- expected_RMS,
- expected_minIndex,
- expected_maxIndex);
+ m_US4DCroppedImage = mitk::IOUtil::Load<mitk::Image>(US4DCroppedFile);
+ CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull());
+ //calculated ground truth via script
+ mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.5398359155908228;
+ mitk::ImageStatisticsContainer::RealType expected_MPP = 157.74074074074073;
+ mitk::ImageStatisticsContainer::RealType expected_max = 199;
+ mitk::ImageStatisticsContainer::RealType expected_mean = 157.74074074074073;
+ mitk::ImageStatisticsContainer::RealType expected_min = 101;
+ mitk::ImageStatisticsContainer::VoxelCountType expected_N = 27;
+ mitk::ImageStatisticsContainer::RealType expected_RMS = 161.11544579426010;
+ mitk::ImageStatisticsContainer::RealType expected_skewness = 0.0347280313508018;
+ mitk::ImageStatisticsContainer::RealType expected_standarddev = 32.803133753432512;
+ mitk::ImageStatisticsContainer::RealType expected_variance = 1076.0455840455834;
+ mitk::ImageStatisticsContainer::IndexType expected_minIndex;
+ expected_minIndex.set_size(3);
+ expected_minIndex[0] = 0;
+ expected_minIndex[1] = 2;
+ expected_minIndex[2] = 0;
+
+ mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
+ expected_maxIndex.set_size(3);
+ expected_maxIndex[0] = 0;
+ expected_maxIndex[1] = 0;
+ expected_maxIndex[2] = 1;
+
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer=mitk::ImageStatisticsContainer::New();
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage));
+ auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1);
+
+ VerifyStatistics(statisticsObjectTimestep1,
+ expected_N,
+ expected_mean,
+ expected_MPP,
+ expected_skewness,
+ expected_kurtosis,
+ expected_variance,
+ expected_standarddev,
+ expected_min,
+ expected_max,
+ expected_RMS,
+ expected_minIndex,
+ expected_maxIndex);
}
// T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
void mitkImageStatisticsCalculatorTestSuite::TestUS4DCroppedBinMaskTimeStep1()
{
- MITK_INFO << std::endl << "Test US4D cropped with binary mask timestep 1:-----------------------------------------------------------------------------------";
-
- std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd");
- m_US4DCroppedImage = mitk::IOUtil::Load<mitk::Image>(US4DCroppedFile);
- CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull());
-
- std::string US4DCroppedBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_croppedBinMask.nrrd");
- m_US4DCroppedBinMask = mitk::IOUtil::Load<mitk::Image>(US4DCroppedBinMaskFile);
- CPPUNIT_ASSERT_MESSAGE("Failed loading US4D binary mask", m_US4DCroppedBinMask.IsNotNull());
- //calculated ground truth via script
- mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.5863739712889191;
- mitk::ImageStatisticsContainer::RealType expected_MPP = 166.75;
- mitk::ImageStatisticsContainer::RealType expected_max = 199;
- mitk::ImageStatisticsContainer::RealType expected_mean = 166.75;
- mitk::ImageStatisticsContainer::RealType expected_min = 120;
- mitk::ImageStatisticsContainer::VoxelCountType expected_N = 4;
- mitk::ImageStatisticsContainer::RealType expected_RMS = 169.70636405273669;
- mitk::ImageStatisticsContainer::RealType expected_skewness = -0.4285540263894276;
- mitk::ImageStatisticsContainer::RealType expected_standarddev = 31.538666744172936;
- mitk::ImageStatisticsContainer::RealType expected_variance = 994.6874999999999;
- mitk::ImageStatisticsContainer::IndexType expected_minIndex;
- expected_minIndex.set_size(3);
- expected_minIndex[0] = 0;
- expected_minIndex[1] = 0;
- expected_minIndex[2] = 2;
-
- mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
- expected_maxIndex.set_size(3);
- expected_maxIndex[0] = 1;
- expected_maxIndex[1] = 1;
- expected_maxIndex[2] = 1;
-
- mitk::ImageMaskGenerator::Pointer imgMask1 = mitk::ImageMaskGenerator::New();
- imgMask1->SetInputImage(m_US4DCroppedImage);
- imgMask1->SetImageMask(m_US4DCroppedBinMask);
-
- mitk::ImageStatisticsContainer::Pointer statisticsContainer=mitk::ImageStatisticsContainer::New();
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, imgMask1.GetPointer(), nullptr, 1));
- auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1);
-
- VerifyStatistics(statisticsObjectTimestep1,
- expected_N,
- expected_mean,
- expected_MPP,
- expected_skewness,
- expected_kurtosis,
- expected_variance,
- expected_standarddev,
- expected_min,
- expected_max,
- expected_RMS,
- expected_minIndex,
- expected_maxIndex);
+ MITK_INFO << std::endl << "Test US4D cropped with binary mask timestep 1:-----------------------------------------------------------------------------------";
+
+ std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd");
+ m_US4DCroppedImage = mitk::IOUtil::Load<mitk::Image>(US4DCroppedFile);
+ CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull());
+
+ std::string US4DCroppedBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_croppedBinMask.nrrd");
+ m_US4DCroppedBinMask = mitk::IOUtil::Load<mitk::Image>(US4DCroppedBinMaskFile);
+ CPPUNIT_ASSERT_MESSAGE("Failed loading US4D binary mask", m_US4DCroppedBinMask.IsNotNull());
+ //calculated ground truth via script
+ mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.5863739712889191;
+ mitk::ImageStatisticsContainer::RealType expected_MPP = 166.75;
+ mitk::ImageStatisticsContainer::RealType expected_max = 199;
+ mitk::ImageStatisticsContainer::RealType expected_mean = 166.75;
+ mitk::ImageStatisticsContainer::RealType expected_min = 120;
+ mitk::ImageStatisticsContainer::VoxelCountType expected_N = 4;
+ mitk::ImageStatisticsContainer::RealType expected_RMS = 170.68043971117487;
+ mitk::ImageStatisticsContainer::RealType expected_skewness = -0.4285540263894276;
+ mitk::ImageStatisticsContainer::RealType expected_standarddev = 36.417715469260287;
+ mitk::ImageStatisticsContainer::RealType expected_variance = 1326.25;
+ mitk::ImageStatisticsContainer::IndexType expected_minIndex;
+ expected_minIndex.set_size(3);
+ expected_minIndex[0] = 0;
+ expected_minIndex[1] = 0;
+ expected_minIndex[2] = 2;
+
+ mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
+ expected_maxIndex.set_size(3);
+ expected_maxIndex[0] = 1;
+ expected_maxIndex[1] = 1;
+ expected_maxIndex[2] = 1;
+
+ mitk::ImageMaskGenerator::Pointer imgMask1 = mitk::ImageMaskGenerator::New();
+ imgMask1->SetInputImage(m_US4DCroppedImage);
+ imgMask1->SetImageMask(m_US4DCroppedBinMask);
+
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer=mitk::ImageStatisticsContainer::New();
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, imgMask1.GetPointer(), nullptr, 1));
+ auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1);
+
+ VerifyStatistics(statisticsObjectTimestep1,
+ expected_N,
+ expected_mean,
+ expected_MPP,
+ expected_skewness,
+ expected_kurtosis,
+ expected_variance,
+ expected_standarddev,
+ expected_min,
+ expected_max,
+ expected_RMS,
+ expected_minIndex,
+ expected_maxIndex);
}
// T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
void mitkImageStatisticsCalculatorTestSuite::TestUS4DCroppedMultilabelMaskTimeStep1()
{
- MITK_INFO << std::endl << "Test US4D cropped with mulitlabel mask timestep 1:-----------------------------------------------------------------------------------";
-
- std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd");
- m_US4DCroppedImage = mitk::IOUtil::Load<mitk::Image>(US4DCroppedFile);
- CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull());
-
- std::string US4DCroppedMultilabelMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_croppedMultilabelMask.nrrd");
- m_US4DCroppedMultilabelMask = mitk::IOUtil::Load<mitk::Image>(US4DCroppedMultilabelMaskFile);
- CPPUNIT_ASSERT_MESSAGE("Failed loading US4D multilabel mask", m_US4DCroppedMultilabelMask.IsNotNull());
- //calculated ground truth via script
- mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.0432484564918287;
- mitk::ImageStatisticsContainer::RealType expected_MPP = 159.75;
- mitk::ImageStatisticsContainer::RealType expected_max = 199;
- mitk::ImageStatisticsContainer::RealType expected_mean = 159.75;
- mitk::ImageStatisticsContainer::RealType expected_min = 120;
- mitk::ImageStatisticsContainer::VoxelCountType expected_N = 4;
- mitk::ImageStatisticsContainer::RealType expected_RMS = 163.74446555532802;
- mitk::ImageStatisticsContainer::RealType expected_skewness = -0.004329226115093;
- mitk::ImageStatisticsContainer::RealType expected_standarddev = 35.947009611371016;
- mitk::ImageStatisticsContainer::RealType expected_variance = 1292.187500000000227;
- mitk::ImageStatisticsContainer::IndexType expected_minIndex;
- expected_minIndex.set_size(3);
- expected_minIndex[0] = 0;
- expected_minIndex[1] = 0;
- expected_minIndex[2] = 2;
-
- mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
- expected_maxIndex.set_size(3);
- expected_maxIndex[0] = 0;
- expected_maxIndex[1] = 0;
- expected_maxIndex[2] = 1;
-
- mitk::ImageMaskGenerator::Pointer imgMask1 = mitk::ImageMaskGenerator::New();
- imgMask1->SetInputImage(m_US4DCroppedImage);
- imgMask1->SetImageMask(m_US4DCroppedMultilabelMask);
-
- mitk::ImageStatisticsContainer::Pointer statisticsContainer;
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, imgMask1.GetPointer(), nullptr, 1));
- auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1);
-
- VerifyStatistics(statisticsObjectTimestep1,
- expected_N,
- expected_mean,
- expected_MPP,
- expected_skewness,
- expected_kurtosis,
- expected_variance,
- expected_standarddev,
- expected_min,
- expected_max,
- expected_RMS,
- expected_minIndex,
- expected_maxIndex);
+ MITK_INFO << std::endl << "Test US4D cropped with mulitlabel mask timestep 1:-----------------------------------------------------------------------------------";
+
+ std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd");
+ m_US4DCroppedImage = mitk::IOUtil::Load<mitk::Image>(US4DCroppedFile);
+ CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull());
+
+ std::string US4DCroppedMultilabelMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_croppedMultilabelMask.nrrd");
+ m_US4DCroppedMultilabelMask = mitk::IOUtil::Load<mitk::Image>(US4DCroppedMultilabelMaskFile);
+ CPPUNIT_ASSERT_MESSAGE("Failed loading US4D multilabel mask", m_US4DCroppedMultilabelMask.IsNotNull());
+ //calculated ground truth via script
+ mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.0432484564918287;
+ mitk::ImageStatisticsContainer::RealType expected_MPP = 159.75;
+ mitk::ImageStatisticsContainer::RealType expected_max = 199;
+ mitk::ImageStatisticsContainer::RealType expected_mean = 159.75;
+ mitk::ImageStatisticsContainer::RealType expected_min = 120;
+ mitk::ImageStatisticsContainer::VoxelCountType expected_N = 4;
+ mitk::ImageStatisticsContainer::RealType expected_RMS = 165.05447333128134;
+ mitk::ImageStatisticsContainer::RealType expected_skewness = -0.004329226115093;
+ mitk::ImageStatisticsContainer::RealType expected_standarddev = 41.508031351374242;
+ mitk::ImageStatisticsContainer::RealType expected_variance = 1722.9166666666670;
+ mitk::ImageStatisticsContainer::IndexType expected_minIndex;
+ expected_minIndex.set_size(3);
+ expected_minIndex[0] = 0;
+ expected_minIndex[1] = 0;
+ expected_minIndex[2] = 2;
+
+ mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
+ expected_maxIndex.set_size(3);
+ expected_maxIndex[0] = 0;
+ expected_maxIndex[1] = 0;
+ expected_maxIndex[2] = 1;
+
+ mitk::ImageMaskGenerator::Pointer imgMask1 = mitk::ImageMaskGenerator::New();
+ imgMask1->SetInputImage(m_US4DCroppedImage);
+ imgMask1->SetImageMask(m_US4DCroppedMultilabelMask);
+
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer;
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, imgMask1.GetPointer(), nullptr, 1));
+ auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1);
+
+ VerifyStatistics(statisticsObjectTimestep1,
+ expected_N,
+ expected_mean,
+ expected_MPP,
+ expected_skewness,
+ expected_kurtosis,
+ expected_variance,
+ expected_standarddev,
+ expected_min,
+ expected_max,
+ expected_RMS,
+ expected_minIndex,
+ expected_maxIndex);
}
// T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
void mitkImageStatisticsCalculatorTestSuite::TestUS4DCroppedPlanarFigureTimeStep1()
{
- MITK_INFO << std::endl << "Test US4D cropped planar figure timestep 1:-----------------------------------------------------------------------------------";
-
- std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd");
- m_US4DCroppedImage = mitk::IOUtil::Load<mitk::Image>(US4DCroppedFile);
- CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull());
-
- std::string US4DCroppedPlanarFigureFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_croppedPF.pf");
- m_US4DCroppedPlanarFigure = mitk::IOUtil::Load<mitk::PlanarFigure>(US4DCroppedPlanarFigureFile);
- CPPUNIT_ASSERT_MESSAGE("Failed loading US4D planar figure", m_US4DCroppedPlanarFigure.IsNotNull());
- //calculated ground truth via script
- mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1;
- mitk::ImageStatisticsContainer::RealType expected_MPP = 172.5;
- mitk::ImageStatisticsContainer::RealType expected_max = 197;
- mitk::ImageStatisticsContainer::RealType expected_mean = 172.5;
- mitk::ImageStatisticsContainer::RealType expected_min = 148;
- mitk::ImageStatisticsContainer::VoxelCountType expected_N = 2;
- mitk::ImageStatisticsContainer::RealType expected_RMS = 174.23116827938679;
- mitk::ImageStatisticsContainer::RealType expected_skewness = 0;
- mitk::ImageStatisticsContainer::RealType expected_standarddev = 24.5;
- mitk::ImageStatisticsContainer::RealType expected_variance = 600.25;
- mitk::ImageStatisticsContainer::IndexType expected_minIndex;
- expected_minIndex.set_size(3);
- expected_minIndex[0] = 2;
- expected_minIndex[1] = 2;
- expected_minIndex[2] = 2;
-
- mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
- expected_maxIndex.set_size(3);
- expected_maxIndex[0] = 2;
- expected_maxIndex[1] = 2;
- expected_maxIndex[2] = 1;
-
- mitk::PlanarFigureMaskGenerator::Pointer pfMaskGen = mitk::PlanarFigureMaskGenerator::New();
- pfMaskGen->SetInputImage(m_US4DCroppedImage);
- pfMaskGen->SetPlanarFigure(m_US4DCroppedPlanarFigure);
-
- mitk::ImageStatisticsContainer::Pointer statisticsContainer;
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, pfMaskGen.GetPointer()));
- auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1);
-
- VerifyStatistics(statisticsObjectTimestep1,
- expected_N,
- expected_mean,
- expected_MPP,
- expected_skewness,
- expected_kurtosis,
- expected_variance,
- expected_standarddev,
- expected_min,
- expected_max,
- expected_RMS,
- expected_minIndex,
- expected_maxIndex);
+ MITK_INFO << std::endl << "Test US4D cropped planar figure timestep 1:-----------------------------------------------------------------------------------";
+
+ std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd");
+ m_US4DCroppedImage = mitk::IOUtil::Load<mitk::Image>(US4DCroppedFile);
+ CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull());
+
+ std::string US4DCroppedPlanarFigureFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_croppedPF.pf");
+ m_US4DCroppedPlanarFigure = mitk::IOUtil::Load<mitk::PlanarFigure>(US4DCroppedPlanarFigureFile);
+ CPPUNIT_ASSERT_MESSAGE("Failed loading US4D planar figure", m_US4DCroppedPlanarFigure.IsNotNull());
+ //calculated ground truth via script
+ mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1;
+ mitk::ImageStatisticsContainer::RealType expected_MPP = 172.5;
+ mitk::ImageStatisticsContainer::RealType expected_max = 197;
+ mitk::ImageStatisticsContainer::RealType expected_mean = 172.5;
+ mitk::ImageStatisticsContainer::RealType expected_min = 148;
+ mitk::ImageStatisticsContainer::VoxelCountType expected_N = 2;
+ mitk::ImageStatisticsContainer::RealType expected_RMS = 175.94530400098776;
+ mitk::ImageStatisticsContainer::RealType expected_skewness = 0;
+ mitk::ImageStatisticsContainer::RealType expected_standarddev = 34.648232278140831;
+ mitk::ImageStatisticsContainer::RealType expected_variance = 1200.5000000000002;
+ mitk::ImageStatisticsContainer::IndexType expected_minIndex;
+ expected_minIndex.set_size(3);
+ expected_minIndex[0] = 2;
+ expected_minIndex[1] = 2;
+ expected_minIndex[2] = 2;
+
+ mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
+ expected_maxIndex.set_size(3);
+ expected_maxIndex[0] = 2;
+ expected_maxIndex[1] = 2;
+ expected_maxIndex[2] = 1;
+
+ mitk::PlanarFigureMaskGenerator::Pointer pfMaskGen = mitk::PlanarFigureMaskGenerator::New();
+ pfMaskGen->SetInputImage(m_US4DCroppedImage);
+ pfMaskGen->SetPlanarFigure(m_US4DCroppedPlanarFigure);
+
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer;
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, pfMaskGen.GetPointer()));
+ auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1);
+
+ VerifyStatistics(statisticsObjectTimestep1,
+ expected_N,
+ expected_mean,
+ expected_MPP,
+ expected_skewness,
+ expected_kurtosis,
+ expected_variance,
+ expected_standarddev,
+ expected_min,
+ expected_max,
+ expected_RMS,
+ expected_minIndex,
+ expected_maxIndex);
}
void mitkImageStatisticsCalculatorTestSuite::TestUS4DCroppedAllTimesteps()
{
- MITK_INFO << std::endl << "Test US4D cropped all timesteps:-----------------------------------------------------------------------------------";
-
- std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd");
- m_US4DCroppedImage = mitk::IOUtil::Load<mitk::Image>(US4DCroppedFile);
- CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull());
- mitk::ImageStatisticsContainer::Pointer statisticsContainer=mitk::ImageStatisticsContainer::New();
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage));
- for (int i = 0; i < 4; i++)
- {
- CPPUNIT_ASSERT_MESSAGE("Error computing statistics for multiple timestep", statisticsContainer->TimeStepExists(i));
- }
+ MITK_INFO << std::endl << "Test US4D cropped all timesteps:-----------------------------------------------------------------------------------";
+
+ std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd");
+ m_US4DCroppedImage = mitk::IOUtil::Load<mitk::Image>(US4DCroppedFile);
+ CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull());
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer=mitk::ImageStatisticsContainer::New();
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage));
+ for (int i = 0; i < 4; i++)
+ {
+ CPPUNIT_ASSERT_MESSAGE("Error computing statistics for multiple timestep", statisticsContainer->TimeStepExists(i));
+ }
}
void mitkImageStatisticsCalculatorTestSuite::TestUS4DCropped3DMask()
{
- MITK_INFO << std::endl << "Test US4D cropped with 3D binary Mask:-----------------------------------------------------------------------------------";
-
- std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd");
- m_US4DCroppedImage = mitk::IOUtil::Load<mitk::Image>(US4DCroppedFile);
- CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull());
-
- std::string US4DCropped3DBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped3DBinMask.nrrd");
- m_US4DCropped3DBinMask = mitk::IOUtil::Load<mitk::Image>(US4DCropped3DBinMaskFile);
- CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D binary mask", m_US4DCropped3DBinMask.IsNotNull());
- //calculated ground truth via script
- mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1;
- mitk::ImageStatisticsContainer::RealType expected_MPP = 198;
- mitk::ImageStatisticsContainer::RealType expected_max = 199;
- mitk::ImageStatisticsContainer::RealType expected_mean = 198;
- mitk::ImageStatisticsContainer::RealType expected_min = 197;
- mitk::ImageStatisticsContainer::VoxelCountType expected_N = 2;
- mitk::ImageStatisticsContainer::RealType expected_RMS = 198.00252523642217;
- mitk::ImageStatisticsContainer::RealType expected_skewness = 0;
- mitk::ImageStatisticsContainer::RealType expected_standarddev = 1;
- mitk::ImageStatisticsContainer::RealType expected_variance = 1;
- mitk::ImageStatisticsContainer::IndexType expected_minIndex;
- expected_minIndex.set_size(3);
- expected_minIndex[0] = 1;
- expected_minIndex[1] = 2;
- expected_minIndex[2] = 1;
-
- mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
- expected_maxIndex.set_size(3);
- expected_maxIndex[0] = 1;
- expected_maxIndex[1] = 1;
- expected_maxIndex[2] = 1;
-
- mitk::ImageMaskGenerator::Pointer imgMask1 = mitk::ImageMaskGenerator::New();
- imgMask1->SetInputImage(m_US4DCroppedImage);
- imgMask1->SetImageMask(m_US4DCropped3DBinMask);
-
- mitk::ImageStatisticsContainer::Pointer statisticsContainer = mitk::ImageStatisticsContainer::New();
- CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, imgMask1.GetPointer(), nullptr, 1));
- auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1);
-
- VerifyStatistics(statisticsObjectTimestep1,
- expected_N,
- expected_mean,
- expected_MPP,
- expected_skewness,
- expected_kurtosis,
- expected_variance,
- expected_standarddev,
- expected_min,
- expected_max,
- expected_RMS,
- expected_minIndex,
- expected_maxIndex);
+ MITK_INFO << std::endl << "Test US4D cropped with 3D binary Mask:-----------------------------------------------------------------------------------";
+
+ std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd");
+ m_US4DCroppedImage = mitk::IOUtil::Load<mitk::Image>(US4DCroppedFile);
+ CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull());
+
+ std::string US4DCropped3DBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped3DBinMask.nrrd");
+ m_US4DCropped3DBinMask = mitk::IOUtil::Load<mitk::Image>(US4DCropped3DBinMaskFile);
+ CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D binary mask", m_US4DCropped3DBinMask.IsNotNull());
+ //calculated ground truth via script
+ mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1;
+ mitk::ImageStatisticsContainer::RealType expected_MPP = 198;
+ mitk::ImageStatisticsContainer::RealType expected_max = 199;
+ mitk::ImageStatisticsContainer::RealType expected_mean = 198;
+ mitk::ImageStatisticsContainer::RealType expected_min = 197;
+ mitk::ImageStatisticsContainer::VoxelCountType expected_N = 2;
+ mitk::ImageStatisticsContainer::RealType expected_RMS = 198.00505044063902;
+ mitk::ImageStatisticsContainer::RealType expected_skewness = 0;
+ mitk::ImageStatisticsContainer::RealType expected_standarddev = 1.4142135623730951;
+ mitk::ImageStatisticsContainer::RealType expected_variance = 2;
+ mitk::ImageStatisticsContainer::IndexType expected_minIndex;
+ expected_minIndex.set_size(3);
+ expected_minIndex[0] = 1;
+ expected_minIndex[1] = 2;
+ expected_minIndex[2] = 1;
+
+ mitk::ImageStatisticsContainer::IndexType expected_maxIndex;
+ expected_maxIndex.set_size(3);
+ expected_maxIndex[0] = 1;
+ expected_maxIndex[1] = 1;
+ expected_maxIndex[2] = 1;
+
+ mitk::ImageMaskGenerator::Pointer imgMask1 = mitk::ImageMaskGenerator::New();
+ imgMask1->SetInputImage(m_US4DCroppedImage);
+ imgMask1->SetImageMask(m_US4DCropped3DBinMask);
+
+ mitk::ImageStatisticsContainer::Pointer statisticsContainer = mitk::ImageStatisticsContainer::New();
+ CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, imgMask1.GetPointer(), nullptr, 1));
+ auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1);
+
+ VerifyStatistics(statisticsObjectTimestep1,
+ expected_N,
+ expected_mean,
+ expected_MPP,
+ expected_skewness,
+ expected_kurtosis,
+ expected_variance,
+ expected_standarddev,
+ expected_min,
+ expected_max,
+ expected_RMS,
+ expected_minIndex,
+ expected_maxIndex);
}
mitk::PlanarPolygon::Pointer mitkImageStatisticsCalculatorTestSuite::GeneratePlanarPolygon(mitk::PlaneGeometry::Pointer geometry, std::vector <mitk::Point2D> points)
{
- mitk::PlanarPolygon::Pointer figure = mitk::PlanarPolygon::New();
- figure->SetPlaneGeometry(geometry);
- figure->PlaceFigure(points[0]);
+ mitk::PlanarPolygon::Pointer figure = mitk::PlanarPolygon::New();
+ figure->SetPlaneGeometry(geometry);
+ figure->PlaceFigure(points[0]);
for (unsigned int i = 1; i < points.size(); i++)
- {
- figure->SetControlPoint(i, points[i], true);
- }
- return figure;
+ {
+ figure->SetControlPoint(i, points[i], true);
+ }
+ return figure;
}
const mitk::ImageStatisticsContainer::Pointer
mitkImageStatisticsCalculatorTestSuite::ComputeStatistics(mitk::Image::ConstPointer image,
- mitk::MaskGenerator::Pointer maskGen,
- mitk::MaskGenerator::Pointer secondardMaskGen,
- unsigned short label)
+ mitk::MaskGenerator::Pointer maskGen,
+ mitk::MaskGenerator::Pointer secondardMaskGen,
+ unsigned short label)
{
- mitk::ImageStatisticsCalculator::Pointer imgStatCalc = mitk::ImageStatisticsCalculator::New();
- imgStatCalc->SetInputImage(image);
-
- if (maskGen.IsNotNull())
- {
- imgStatCalc->SetMask(maskGen.GetPointer());
- if (secondardMaskGen.IsNotNull())
- {
- imgStatCalc->SetSecondaryMask(secondardMaskGen.GetPointer());
- }
- }
-
- return imgStatCalc->GetStatistics(label);
+ mitk::ImageStatisticsCalculator::Pointer imgStatCalc = mitk::ImageStatisticsCalculator::New();
+ imgStatCalc->SetInputImage(image);
+
+ if (maskGen.IsNotNull())
+ {
+ imgStatCalc->SetMask(maskGen.GetPointer());
+ if (secondardMaskGen.IsNotNull())
+ {
+ imgStatCalc->SetSecondaryMask(secondardMaskGen.GetPointer());
+ }
+ }
+
+ return imgStatCalc->GetStatistics(label);
}
void mitkImageStatisticsCalculatorTestSuite::VerifyStatistics(mitk::ImageStatisticsContainer::ImageStatisticsObject stats,
- mitk::ImageStatisticsContainer::RealType testMean, mitk::ImageStatisticsContainer::RealType testSD, mitk::ImageStatisticsContainer::RealType testMedian)
+ mitk::ImageStatisticsContainer::RealType testMean, mitk::ImageStatisticsContainer::RealType testSD, mitk::ImageStatisticsContainer::RealType testMedian)
{
mitk::ImageStatisticsContainer::RealType meanObject = 0;
mitk::ImageStatisticsContainer::RealType standardDeviationObject = 0;
mitk::ImageStatisticsContainer::RealType medianObject = 0;
- CPPUNIT_ASSERT_NO_THROW(meanObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MEAN()));
- CPPUNIT_ASSERT_NO_THROW(standardDeviationObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::STANDARDDEVIATION()));
- CPPUNIT_ASSERT_NO_THROW(medianObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MEDIAN()));
- CPPUNIT_ASSERT_MESSAGE("Calculated mean grayvalue is not equal to the desired value.", std::abs(meanObject - testMean) < mitk::eps);
- CPPUNIT_ASSERT_MESSAGE("Calculated grayvalue sd is not equal to the desired value.", std::abs(standardDeviationObject - testSD) < mitk::eps);
- CPPUNIT_ASSERT_MESSAGE("Calculated median grayvalue is not equal to the desired value.", std::abs(medianObject - testMedian) < mitk::eps);
+ CPPUNIT_ASSERT_NO_THROW(meanObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MEAN()));
+ CPPUNIT_ASSERT_NO_THROW(standardDeviationObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::STANDARDDEVIATION()));
+ CPPUNIT_ASSERT_NO_THROW(medianObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MEDIAN()));
+ CPPUNIT_ASSERT_MESSAGE("Calculated mean grayvalue is not equal to the desired value.", std::abs(meanObject - testMean) < mitk::eps);
+ CPPUNIT_ASSERT_MESSAGE("Calculated grayvalue sd is not equal to the desired value.", std::abs(standardDeviationObject - testSD) < mitk::eps);
+ CPPUNIT_ASSERT_MESSAGE("Calculated median grayvalue is not equal to the desired value.", std::abs(medianObject - testMedian) < mitk::eps);
}
// T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy)
void mitkImageStatisticsCalculatorTestSuite::VerifyStatistics(mitk::ImageStatisticsContainer::ImageStatisticsObject stats,
- mitk::ImageStatisticsContainer::VoxelCountType N,
- mitk::ImageStatisticsContainer::RealType mean,
- mitk::ImageStatisticsContainer::RealType MPP,
- mitk::ImageStatisticsContainer::RealType skewness,
- mitk::ImageStatisticsContainer::RealType kurtosis,
- mitk::ImageStatisticsContainer::RealType variance,
- mitk::ImageStatisticsContainer::RealType stdev,
- mitk::ImageStatisticsContainer::RealType min,
- mitk::ImageStatisticsContainer::RealType max,
- mitk::ImageStatisticsContainer::RealType RMS,
- mitk::ImageStatisticsContainer::IndexType minIndex,
- mitk::ImageStatisticsContainer::IndexType maxIndex)
+ mitk::ImageStatisticsContainer::VoxelCountType N,
+ mitk::ImageStatisticsContainer::RealType mean,
+ mitk::ImageStatisticsContainer::RealType MPP,
+ mitk::ImageStatisticsContainer::RealType skewness,
+ mitk::ImageStatisticsContainer::RealType kurtosis,
+ mitk::ImageStatisticsContainer::RealType variance,
+ mitk::ImageStatisticsContainer::RealType stdev,
+ mitk::ImageStatisticsContainer::RealType min,
+ mitk::ImageStatisticsContainer::RealType max,
+ mitk::ImageStatisticsContainer::RealType RMS,
+ mitk::ImageStatisticsContainer::IndexType minIndex,
+ mitk::ImageStatisticsContainer::IndexType maxIndex)
{
mitk::ImageStatisticsContainer::VoxelCountType numberOfVoxelsObject;
mitk::ImageStatisticsContainer::RealType meanObject = 0;
mitk::ImageStatisticsContainer::RealType mppObject = 0;
mitk::ImageStatisticsContainer::RealType skewnessObject = 0;
mitk::ImageStatisticsContainer::RealType kurtosisObject = 0;
mitk::ImageStatisticsContainer::RealType varianceObject = 0;
mitk::ImageStatisticsContainer::RealType standardDeviationObject = 0;
mitk::ImageStatisticsContainer::RealType minObject = 0;
mitk::ImageStatisticsContainer::RealType maxObject = 0;
mitk::ImageStatisticsContainer::RealType rmsObject = 0;
mitk::ImageStatisticsContainer::IndexType minIndexObject(3,0);
mitk::ImageStatisticsContainer::IndexType maxIndexObject(3,0);
- CPPUNIT_ASSERT_NO_THROW(numberOfVoxelsObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::VoxelCountType>(mitk::ImageStatisticsConstants::NUMBEROFVOXELS()));
- CPPUNIT_ASSERT_NO_THROW(meanObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MEAN()));
- CPPUNIT_ASSERT_NO_THROW(mppObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MPP()));
- CPPUNIT_ASSERT_NO_THROW(skewnessObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::SKEWNESS()));
- CPPUNIT_ASSERT_NO_THROW(kurtosisObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::KURTOSIS()));
- CPPUNIT_ASSERT_NO_THROW(varianceObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::VARIANCE()));
- CPPUNIT_ASSERT_NO_THROW(standardDeviationObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::STANDARDDEVIATION()));
- CPPUNIT_ASSERT_NO_THROW(minObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MINIMUM()));
- CPPUNIT_ASSERT_NO_THROW(maxObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MAXIMUM()));
- CPPUNIT_ASSERT_NO_THROW(rmsObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::RMS()));
- CPPUNIT_ASSERT_NO_THROW(minIndexObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::IndexType>(mitk::ImageStatisticsConstants::MINIMUMPOSITION()));
- CPPUNIT_ASSERT_NO_THROW(maxIndexObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::IndexType>(mitk::ImageStatisticsConstants::MAXIMUMPOSITION()));
-
- CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", numberOfVoxelsObject - N == 0);
- CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(meanObject - mean) < mitk::eps);
- // in three test cases MPP is None because the roi has no positive pixels
- if (!std::isnan(mppObject))
- {
- CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(mppObject - MPP) < mitk::eps);
- }
- CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(skewnessObject - skewness) < mitk::eps);
- CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(kurtosisObject - kurtosis) < mitk::eps);
- CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(varianceObject - variance) < mitk::eps);
- CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(standardDeviationObject - stdev) < mitk::eps);
- CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(minObject - min) < mitk::eps);
- CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(maxObject - max) < mitk::eps);
- CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(rmsObject - RMS) < mitk::eps);
- for (unsigned int i = 0; i < minIndex.size(); ++i)
- {
- CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(minIndexObject[i] - minIndex[i]) < mitk::eps);
- }
- for (unsigned int i = 0; i < maxIndex.size(); ++i)
- {
- CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(maxIndexObject[i] - maxIndex[i]) < mitk::eps);
- }
+ CPPUNIT_ASSERT_NO_THROW(numberOfVoxelsObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::VoxelCountType>(mitk::ImageStatisticsConstants::NUMBEROFVOXELS()));
+ CPPUNIT_ASSERT_NO_THROW(meanObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MEAN()));
+ CPPUNIT_ASSERT_NO_THROW(mppObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MPP()));
+ CPPUNIT_ASSERT_NO_THROW(skewnessObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::SKEWNESS()));
+ CPPUNIT_ASSERT_NO_THROW(kurtosisObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::KURTOSIS()));
+ CPPUNIT_ASSERT_NO_THROW(varianceObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::VARIANCE()));
+ if (std::abs(varianceObject - variance) >= mitk::eps)
+ MITK_INFO << "xxx";
+ CPPUNIT_ASSERT_NO_THROW(standardDeviationObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::STANDARDDEVIATION()));
+ CPPUNIT_ASSERT_NO_THROW(minObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MINIMUM()));
+ CPPUNIT_ASSERT_NO_THROW(maxObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MAXIMUM()));
+ CPPUNIT_ASSERT_NO_THROW(rmsObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::RMS()));
+ CPPUNIT_ASSERT_NO_THROW(minIndexObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::IndexType>(mitk::ImageStatisticsConstants::MINIMUMPOSITION()));
+ CPPUNIT_ASSERT_NO_THROW(maxIndexObject = stats.GetValueConverted<mitk::ImageStatisticsContainer::IndexType>(mitk::ImageStatisticsConstants::MAXIMUMPOSITION()));
+
+ CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", numberOfVoxelsObject - N == 0);
+ CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(meanObject - mean) < mitk::eps);
+ // in three test cases MPP is None because the roi has no positive pixels
+ if (!std::isnan(mppObject))
+ {
+ CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(mppObject - MPP) < mitk::eps);
+ }
+ CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(skewnessObject - skewness) < mitk::eps);
+ CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(kurtosisObject - kurtosis) < mitk::eps);
+ CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(varianceObject - variance) < mitk::eps);
+ CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(standardDeviationObject - stdev) < mitk::eps);
+ CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(minObject - min) < mitk::eps);
+ CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(maxObject - max) < mitk::eps);
+ CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(rmsObject - RMS) < mitk::eps);
+ for (unsigned int i = 0; i < minIndex.size(); ++i)
+ {
+ CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(minIndexObject[i] - minIndex[i]) < mitk::eps);
+ }
+ for (unsigned int i = 0; i < maxIndex.size(); ++i)
+ {
+ CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(maxIndexObject[i] - maxIndex[i]) < mitk::eps);
+ }
}
MITK_TEST_SUITE_REGISTRATION(mitkImageStatisticsCalculator)
diff --git a/Modules/ImageStatistics/Testing/mitkImageStatisticsTextureAnalysisTest.cpp b/Modules/ImageStatistics/Testing/mitkImageStatisticsTextureAnalysisTest.cpp
index f55425c872..f517de9074 100644
--- a/Modules/ImageStatistics/Testing/mitkImageStatisticsTextureAnalysisTest.cpp
+++ b/Modules/ImageStatistics/Testing/mitkImageStatisticsTextureAnalysisTest.cpp
@@ -1,230 +1,239 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkTestingMacros.h"
#include "itkImage.h"
-#include "mitkExtendedLabelStatisticsImageFilter.h"
-#include "mitkExtendedStatisticsImageFilter.h"
+#include "mitkLabelStatisticsImageFilter.h"
+#include "mitkStatisticsImageFilter.h"
#include "mitkNumericConstants.h"
/**
\section Testing of Skewness and Kurtosis
* This test class is for testing the added coefficients Skewness and Kurtosis
* for the mitkExtendedLabelStatisticsImageFilter (Masked Images) and for the
* mitkExtendedStatisticsImageFilter (Unmasked Images). Both filter will be tested
* against two pictures.
*/
class mitkImageStatisticsTextureAnalysisTestClass
{
/**
* \brief Explanation of the mitkImageStatisticsTextureAnalysisTestClass test class
*
* this test class produce test images and masking images with the method CreatingTestImageForDifferentLabelSize.
* TestInstanceFortheMaskedStatisticsFilter and TestInstanceFortheUnmaskedStatisticsFilter are the two Instances
* for the filters of masking and unmasking images.
* TestofSkewnessAndKurtosisForMaskedImagesand TestofSkewnessAndKurtosisForUnmaskedImages are the correlated test
* for the checking the values.
*/
public:
- typedef itk::Image< int,3 >ImageType;
+ typedef itk::Image< mitk::Label::PixelType,3 >ImageType;
typedef ImageType::Pointer PointerOfImage;
- typedef itk::ExtendedLabelStatisticsImageFilter< ImageType, ImageType > LabelStatisticsFilterType;
+ typedef mitk::LabelStatisticsImageFilter< ImageType > LabelStatisticsFilterType;
typedef LabelStatisticsFilterType::Pointer labelStatisticsFilterPointer;
- typedef itk::ExtendedStatisticsImageFilter< ImageType > StatisticsFilterType;
+ typedef mitk::StatisticsImageFilter< ImageType > StatisticsFilterType;
typedef StatisticsFilterType::Pointer StatisticsFilterPointer;
ImageType::Pointer CreatingTestImageForDifferentLabelSize( int factorOfDividingThePicture, int bufferValue, int labelValue)
{
ImageType::Pointer image = ImageType :: New();
ImageType::IndexType start;
ImageType::SizeType size;
start[0] = 0;
start[1] = 0;
start[2] = 0;
size[0] = 100;
size[1] = 100;
size[2] = 100;
ImageType:: RegionType region;
region.SetSize( size );
region.SetIndex( start );
image->SetRegions(region);
image->Allocate();
image->FillBuffer(bufferValue);
for(unsigned int r = 0; r < 50; r++)
{
for(int c = 0; c < factorOfDividingThePicture; c++)
{
for(unsigned int l = 0; l < 100; l++)
{
ImageType::IndexType pixelIndex;
pixelIndex[0] = r;
pixelIndex[1] = c;
pixelIndex[2] = l;
image->SetPixel(pixelIndex, labelValue);
}
}
}
return image;
}
LabelStatisticsFilterType::Pointer TestInstanceFortheMaskedStatisticsFilter(ImageType::Pointer image, ImageType::Pointer maskImage)
{
LabelStatisticsFilterType::Pointer labelStatisticsFilter;
labelStatisticsFilter = LabelStatisticsFilterType::New();
labelStatisticsFilter->SetInput( image );
- labelStatisticsFilter->UseHistogramsOn();
- labelStatisticsFilter->SetHistogramParameters( 20, -10, 10);
+
+ std::unordered_map<mitk::Label::PixelType, unsigned int> sizes;
+ sizes[1] = 20;
+
+ std::unordered_map<mitk::Label::PixelType, LabelStatisticsFilterType::RealType> lowerBounds;
+ lowerBounds[1] = -10;
+
+ std::unordered_map<mitk::Label::PixelType, LabelStatisticsFilterType::RealType> upperBounds;
+ upperBounds[1] = 10;
+
+ labelStatisticsFilter->SetHistogramParameters(sizes, lowerBounds, upperBounds);
labelStatisticsFilter->SetLabelInput( maskImage );
labelStatisticsFilter->Update();
return labelStatisticsFilter;
}
StatisticsFilterType::Pointer TestInstanceFortheUnmaskedStatisticsFilter(ImageType::Pointer image )
{
StatisticsFilterType::Pointer StatisticsFilter;
StatisticsFilter = StatisticsFilterType::New();
StatisticsFilter->SetInput( image );
StatisticsFilter->SetHistogramParameters( 20, -10, 10 );
StatisticsFilter->Update();
return StatisticsFilter;
}
//test for Skewness,Kurtosis and MPP for masked Images
void TestofSkewnessKurtosisAndMPPForMaskedImages(LabelStatisticsFilterType::Pointer labelStatisticsFilter, double expectedSkewness, double expectedKurtosis, double expectedMPP)
{
// let's create an object of our class
bool isSkewsnessLowerlimitCorrect = labelStatisticsFilter->GetSkewness( 1 )- expectedKurtosis+ std::pow(10,-3) <= expectedSkewness;
bool isSkewsnessUpperlimitCorrect = labelStatisticsFilter->GetSkewness( 1 )+ expectedKurtosis+ std::pow(10,-3) >= expectedSkewness;
MITK_TEST_CONDITION( isSkewsnessLowerlimitCorrect && isSkewsnessUpperlimitCorrect,"expectedSkewness: " << expectedSkewness << " actual Value: " << labelStatisticsFilter->GetSkewness( 1 ) );
bool isKurtosisUpperlimitCorrect = labelStatisticsFilter->GetKurtosis( 1 ) <= expectedKurtosis+ std::pow(10,-3);
bool isKurtosisLowerlimitCorrect = expectedKurtosis- std::pow(10,-3) <= labelStatisticsFilter->GetKurtosis( 1 );
MITK_TEST_CONDITION( isKurtosisUpperlimitCorrect && isKurtosisLowerlimitCorrect,"expectedKurtosis: " << expectedKurtosis << " actual Value: " << labelStatisticsFilter->GetKurtosis( 1 ) );
MITK_TEST_CONDITION( ( expectedMPP - labelStatisticsFilter->GetMPP( 1 ) ) < 1, "expected MPP: " << expectedMPP << " actual Value: " << labelStatisticsFilter->GetMPP( 1 ) );
}
//test for Entropy,Uniformity and UPP for masked Images
void TestofEntropyUniformityAndUppForMaskedImages(LabelStatisticsFilterType::Pointer labelStatisticsFilter, double expectedEntropy, double expectedUniformity, double expectedUPP)
{
bool calculatedEntropyLowerLimit = labelStatisticsFilter->GetEntropy( 1 ) >= expectedEntropy - std::pow(10,-3);
bool calculatedUniformityLowerLimit = labelStatisticsFilter->GetUniformity( 1 ) >= expectedUniformity - std::pow(10,-3);
bool calculatedUppLowerLimit = labelStatisticsFilter->GetUPP( 1 ) >= expectedUPP - std::pow(10,-3);
bool calculatedEntropyUpperLimit = labelStatisticsFilter->GetEntropy( 1 ) <= expectedEntropy + std::pow(10,-3);
bool calculatedUniformityUpperLimit = labelStatisticsFilter->GetUniformity( 1 ) <= expectedUniformity + std::pow(10,-3);
bool calculatedUppUpperLimit = labelStatisticsFilter->GetUPP( 1 ) <= expectedUPP + std::pow(10,-3);
MITK_TEST_CONDITION( calculatedEntropyLowerLimit && calculatedEntropyUpperLimit, "expected Entropy: " << expectedEntropy << " actual Value: " << labelStatisticsFilter->GetEntropy( 1 ) );
MITK_TEST_CONDITION( calculatedUniformityLowerLimit && calculatedUniformityUpperLimit, "expected Uniformity: " << expectedUniformity << " actual Value: " << labelStatisticsFilter->GetUniformity( 1 ) );
MITK_TEST_CONDITION( calculatedUppLowerLimit && calculatedUppUpperLimit, "expected UPP: " << expectedUPP << " actual Value: " << labelStatisticsFilter->GetUPP( 1 ) );
}
//test for Skewness,Kurtosis and MPP for unmasked Images
void TestofSkewnessKurtosisAndMPPForUnmaskedImages(StatisticsFilterType::Pointer StatisticsFilter, double expectedSkewness, double expectedKurtosis, double expectedMPP)
{
// let's create an object of our class
bool isSkewsnessLowerlimitCorrect = StatisticsFilter->GetSkewness()- expectedKurtosis+ std::pow(10,-3) <= expectedSkewness;
bool isSkewsnessUpperlimitCorrect = StatisticsFilter->GetSkewness()+ expectedKurtosis+ std::pow(10,-3) >= expectedSkewness;
MITK_TEST_CONDITION( isSkewsnessLowerlimitCorrect && isSkewsnessUpperlimitCorrect,"expectedSkewness: " << expectedSkewness << " actual Value: " << StatisticsFilter->GetSkewness() );
bool isKurtosisUpperlimitCorrect = StatisticsFilter->GetKurtosis() <= expectedKurtosis+ std::pow(10,-3);
bool isKurtosisLowerlimitCorrect = expectedKurtosis- std::pow(10,-3) <= StatisticsFilter->GetKurtosis();
MITK_TEST_CONDITION( isKurtosisUpperlimitCorrect && isKurtosisLowerlimitCorrect,"expectedKurtosis: " << expectedKurtosis << " actual Value: " << StatisticsFilter->GetKurtosis() );
MITK_TEST_CONDITION( ( expectedMPP - StatisticsFilter->GetMPP() ) < mitk::eps, "expected MPP: " << expectedMPP << " actual Value: " << StatisticsFilter->GetMPP() );
}
//test for Entropy,Uniformity and UPP for unmasked Images
void TestofEntropyUniformityAndUppForUnmaskedImages(StatisticsFilterType::Pointer StatisticsFilter, double expectedEntropy, double expectedUniformity, double expectedUPP)
{
bool calculatedEntropyLowerLimit = StatisticsFilter->GetEntropy() >= expectedEntropy - std::pow(10,-3);
bool calculatedUniformityLowerLimit = StatisticsFilter->GetUniformity() >= expectedUniformity - std::pow(10,-3);
bool calculatedUppLowerLimit = StatisticsFilter->GetUPP() >= expectedUPP - std::pow(10,-3);
bool calculatedEntropyUpperLimit = StatisticsFilter->GetEntropy() <= expectedEntropy + std::pow(10,-3);
bool calculatedUniformityUpperLimit = StatisticsFilter->GetUniformity() <= expectedUniformity + std::pow(10,-3);
bool calculatedUppUpperLimit = StatisticsFilter->GetUPP() <= expectedUPP + std::pow(10,-3);
MITK_TEST_CONDITION( calculatedEntropyLowerLimit && calculatedEntropyUpperLimit, "expected Entropy: " << expectedEntropy << " actual Value: " << StatisticsFilter->GetEntropy() );
MITK_TEST_CONDITION( calculatedUniformityLowerLimit && calculatedUniformityUpperLimit, "expected Uniformity: " << expectedUniformity << " actual Value: " << StatisticsFilter->GetUniformity() );
MITK_TEST_CONDITION( calculatedUppLowerLimit && calculatedUppUpperLimit, "expected UPP: " << expectedUPP << " actual Value: " << StatisticsFilter->GetUPP() );
}
};
int mitkImageStatisticsTextureAnalysisTest(int, char* [])
{
// always start with this!
MITK_TEST_BEGIN("mitkImageStatisticsTextureAnalysisTest")
mitkImageStatisticsTextureAnalysisTestClass testclassInstance;
mitkImageStatisticsTextureAnalysisTestClass::PointerOfImage labelImage = testclassInstance.CreatingTestImageForDifferentLabelSize(100, 1, 1);
mitkImageStatisticsTextureAnalysisTestClass::PointerOfImage image = testclassInstance.CreatingTestImageForDifferentLabelSize(100, 3, 2);
mitkImageStatisticsTextureAnalysisTestClass::PointerOfImage image2 = testclassInstance.CreatingTestImageForDifferentLabelSize(50, 3, 2);
//test for masked images
mitkImageStatisticsTextureAnalysisTestClass::labelStatisticsFilterPointer mitkLabelFilter= testclassInstance.TestInstanceFortheMaskedStatisticsFilter( image,labelImage);
testclassInstance.TestofSkewnessKurtosisAndMPPForMaskedImages(mitkLabelFilter, 0, 0.999998, 2.5);
testclassInstance.TestofEntropyUniformityAndUppForMaskedImages(mitkLabelFilter, 1, 0.5, 0.5);
mitkImageStatisticsTextureAnalysisTestClass::labelStatisticsFilterPointer mitkLabelFilter2= testclassInstance.TestInstanceFortheMaskedStatisticsFilter( image2,labelImage);
testclassInstance.TestofSkewnessKurtosisAndMPPForMaskedImages(mitkLabelFilter2, -1.1547, 2.33333, 2.75);
testclassInstance.TestofEntropyUniformityAndUppForMaskedImages(mitkLabelFilter2, 0.811278, 0.625, 0.625);
//test for unmasked images
mitkImageStatisticsTextureAnalysisTestClass::StatisticsFilterPointer mitkFilter= testclassInstance.TestInstanceFortheUnmaskedStatisticsFilter( image);
testclassInstance.TestofSkewnessKurtosisAndMPPForUnmaskedImages(mitkFilter, 0, 0.999998, 2.5);
testclassInstance.TestofEntropyUniformityAndUppForUnmaskedImages(mitkFilter, 1, 0.5, 0.5);
mitkImageStatisticsTextureAnalysisTestClass::StatisticsFilterPointer mitkFilter2= testclassInstance.TestInstanceFortheUnmaskedStatisticsFilter( image2);
testclassInstance.TestofSkewnessKurtosisAndMPPForUnmaskedImages(mitkFilter2, -1.1547, 2.33333, 2.75);
testclassInstance.TestofEntropyUniformityAndUppForUnmaskedImages( mitkFilter2, 0.811278, 0.625, 0.625);
MITK_TEST_END()
}
diff --git a/Modules/ImageStatistics/files.cmake b/Modules/ImageStatistics/files.cmake
index 454e2941ce..3d89557ffe 100644
--- a/Modules/ImageStatistics/files.cmake
+++ b/Modules/ImageStatistics/files.cmake
@@ -1,50 +1,50 @@
set(CPP_FILES
mitkImageStatisticsCalculator.cpp
mitkImageStatisticsContainer.cpp
mitkPointSetStatisticsCalculator.cpp
mitkPointSetDifferenceStatisticsCalculator.cpp
mitkIntensityProfile.cpp
mitkHotspotMaskGenerator.cpp
mitkMaskGenerator.cpp
mitkPlanarFigureMaskGenerator.cpp
mitkMultiLabelMaskGenerator.cpp
mitkImageMaskGenerator.cpp
mitkHistogramStatisticsCalculator.cpp
mitkIgnorePixelMaskGenerator.cpp
mitkImageStatisticsPredicateHelper.cpp
mitkImageStatisticsContainerNodeHelper.cpp
mitkImageStatisticsContainerManager.cpp
mitkStatisticsToImageRelationRule.cpp
mitkStatisticsToMaskRelationRule.cpp
mitkImageStatisticsConstants.cpp
)
set(H_FILES
mitkImageStatisticsCalculator.h
mitkImageStatisticsContainer.h
mitkPointSetDifferenceStatisticsCalculator.h
mitkPointSetStatisticsCalculator.h
- mitkExtendedStatisticsImageFilter.h
- mitkExtendedLabelStatisticsImageFilter.h
+ mitkStatisticsImageFilter.h
+ mitkLabelStatisticsImageFilter.h
mitkHotspotMaskGenerator.h
mitkMaskGenerator.h
mitkPlanarFigureMaskGenerator.h
mitkMultiLabelMaskGenerator.h
mitkImageMaskGenerator.h
mitkHistogramStatisticsCalculator.h
mitkMaskUtilities.h
mitkitkMaskImageFilter.h
mitkIgnorePixelMaskGenerator.h
mitkMinMaxImageFilterWithIndex.h
mitkMinMaxLabelmageFilterWithIndex.h
mitkImageStatisticsPredicateHelper.h
mitkImageStatisticsContainerNodeHelper.h
mitkImageStatisticsContainerManager.h
mitkStatisticsToImageRelationRule.h
mitkStatisticsToMaskRelationRule.h
mitkImageStatisticsConstants.h
)
set(TPP_FILES
mitkMaskUtilities.tpp
-)
\ No newline at end of file
+)
diff --git a/Modules/ImageStatistics/itkMultiGaussianImageSource.hxx b/Modules/ImageStatistics/itkMultiGaussianImageSource.hxx
index d301d0d974..706ebb35c1 100644
--- a/Modules/ImageStatistics/itkMultiGaussianImageSource.hxx
+++ b/Modules/ImageStatistics/itkMultiGaussianImageSource.hxx
@@ -1,1003 +1,998 @@
/*=========================================================================
*
* Copyright Insight Software Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/
/*=========================================================================
*
* Portions of this file are subject to the VTK Toolkit Version 3 copyright.
*
* Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
*
* For complete copyright, license and disclaimer of warranty information
* please refer to the NOTICE file at the top of the ITK source tree.
*
*=========================================================================*/
#ifndef __itkMultiGaussianImageSource_hxx
#define __itkMultiGaussianImageSource_hxx
#include <iostream>
#include <fstream>
#include <ctime>
#include "itkMultiGaussianImageSource.h"
#include "itkImageRegionIterator.h"
#include "itkObjectFactory.h"
#include "itkProgressReporter.h"
#include "itkDOMNodeXMLWriter.h"
#include <cstdlib>
namespace itk
{
/**
*
*/
template< class TOutputImage >
MultiGaussianImageSource< TOutputImage >
::MultiGaussianImageSource()
{
//Initial image is 100 wide in each direction.
for ( unsigned int i = 0; i < TOutputImage::GetImageDimension(); i++ )
{
m_Size[i] = 100;
m_Spacing[i] = 1.0;
m_Origin[i] = 0.0;
m_SphereMidpoint[i] = 0;
}
m_NumberOfGaussians = 0;
m_Radius = 1;
m_RadiusStepNumber = 5;
m_MeanValue = 0;
m_Min = NumericTraits< OutputImagePixelType >::NonpositiveMin();
m_Max = NumericTraits< OutputImagePixelType >::max();
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
MultiGaussianImageSource< TOutputImage >
::~MultiGaussianImageSource()
{}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::SetSize(SizeValueArrayType sizeArray)
{
const unsigned int count = TOutputImage::ImageDimension;
unsigned int i;
for ( i = 0; i < count; i++ )
{
if ( sizeArray[i] != this->m_Size[i] )
{
break;
}
}
if ( i < count )
{
this->Modified();
for ( i = 0; i < count; i++ )
{
this->m_Size[i] = sizeArray[i];
}
}
}
template< class TOutputImage >
const typename MultiGaussianImageSource< TOutputImage >::SizeValueType *
MultiGaussianImageSource< TOutputImage >
::GetSize() const
{
return this->m_Size.GetSize();
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::SetSpacing(SpacingValueArrayType spacingArray)
{
const unsigned int count = TOutputImage::ImageDimension;
unsigned int i;
for ( i = 0; i < count; i++ )
{
if ( spacingArray[i] != this->m_Spacing[i] )
{
break;
}
}
if ( i < count )
{
this->Modified();
for ( i = 0; i < count; i++ )
{
this->m_Spacing[i] = spacingArray[i];
}
}
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::SetOrigin(PointValueArrayType originArray)
{
const unsigned int count = TOutputImage::ImageDimension;
unsigned int i;
for ( i = 0; i < count; i++ )
{
if ( originArray[i] != this->m_Origin[i] )
{
break;
}
}
if ( i < count )
{
this->Modified();
for ( i = 0; i < count; i++ )
{
this->m_Origin[i] = originArray[i];
}
}
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
const typename MultiGaussianImageSource< TOutputImage >::PointValueType *
MultiGaussianImageSource< TOutputImage >
::GetOrigin() const
{
for ( unsigned int i = 0; i < TOutputImage::ImageDimension; i++ )
{
this->m_OriginArray[i] = this->m_Origin[i];
}
return this->m_OriginArray;
}
template< class TOutputImage >
const typename MultiGaussianImageSource< TOutputImage >::SpacingValueType *
MultiGaussianImageSource< TOutputImage >
::GetSpacing() const
{
for ( unsigned int i = 0; i < TOutputImage::ImageDimension; i++ )
{
this->m_SpacingArray[i] = this->m_Spacing[i];
}
return this->m_SpacingArray;
}
//-----------------------------------------------------------------------------------------------------------------------
/**
*
*/
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::PrintSelf(std::ostream & os, Indent indent) const
{
Superclass::PrintSelf(os, indent);
os << indent << "Max: "
<< static_cast< typename NumericTraits< OutputImagePixelType >::PrintType >( m_Max )
<< std::endl;
os << indent << "Min: "
<< static_cast< typename NumericTraits< OutputImagePixelType >::PrintType >( m_Min )
<< std::endl;
os << indent << "Origin: [";
unsigned int ii = 0;
while( ii < TOutputImage::ImageDimension - 1 )
{
os << m_Origin[ii] << ", ";
++ii;
}
os << m_Origin[ii] << "]" << std::endl;
os << indent << "Spacing: [";
ii = 0;
while( ii < TOutputImage::ImageDimension - 1 )
{
os << m_Spacing[ii] << ", ";
++ii;
}
os << m_Spacing[ii] << "]" << std::endl;
os << indent << "Size: [";
ii = 0;
while( ii < TOutputImage::ImageDimension - 1 )
{
os << m_Size[ii] << ", ";
++ii;
}
os << m_Size[ii] << "]" << std::endl;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
unsigned int
MultiGaussianImageSource< TOutputImage >
::GetNumberOfGaussians() const
{
return this->m_NumberOfGaussians;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
typename MultiGaussianImageSource< TOutputImage >::RadiusType
MultiGaussianImageSource< TOutputImage >
::GetRadius() const
{
return this->m_Radius;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::SetRadius( RadiusType radius )
{
this->m_Radius = radius;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::SetNumberOfGausssians( unsigned int n )
{
this->m_NumberOfGaussians = n;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::SetRegionOfInterest( ItkVectorType roiMin, ItkVectorType roiMax )
{
m_RegionOfInterestMax = roiMax;
m_RegionOfInterestMin = roiMin;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
const typename MultiGaussianImageSource< TOutputImage >::OutputImagePixelType
MultiGaussianImageSource< TOutputImage >
::GetMaxMeanValue() const
{
return m_MeanValue;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
const typename MultiGaussianImageSource< TOutputImage >::OutputImagePixelType
MultiGaussianImageSource< TOutputImage >
::GetMaxValueInSphere() const
{
return m_MaxValueInSphere;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
const typename MultiGaussianImageSource< TOutputImage >::IndexType
MultiGaussianImageSource< TOutputImage >
::GetMaxValueIndexInSphere() const
{
return m_MaxValueIndexInSphere;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
const typename MultiGaussianImageSource< TOutputImage >::OutputImagePixelType
MultiGaussianImageSource< TOutputImage >
::GetMinValueInSphere() const
{
return m_MinValueInSphere;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
const typename MultiGaussianImageSource< TOutputImage >::IndexType
MultiGaussianImageSource< TOutputImage >
::GetMinValueIndexInSphere() const
{
return m_MinValueIndexInSphere;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
const typename MultiGaussianImageSource< TOutputImage >::IndexType
MultiGaussianImageSource< TOutputImage >
::GetSphereMidpoint() const
{
return m_SphereMidpoint;
}
//-----------------------------------------------------------------------------------------------------------------------
/* Calculate and return value of the integral of the gaussian in a cuboid region with the dimension 3: in the x-axis between xMin and xMax and in the y-axis between yMin and yMax and in the z-axis also between zMin and zMax. */
template< class TOutputImage >
double
MultiGaussianImageSource< TOutputImage >
::MultiGaussianFunctionValueAtCuboid(double xMin, double xMax, double yMin, double yMax, double zMin, double zMax)
{
double mean = 0;
double summand0, summand1, summand2, value, factor;
for(unsigned int n = 0; n < m_NumberOfGaussians; ++n)
{
summand0 = FunctionPhi((xMax - m_CenterX[n]) / m_SigmaX[n] ) - FunctionPhi((xMin - m_CenterX[n]) / m_SigmaX[n] );
summand1 = FunctionPhi((yMax - m_CenterY[n]) / m_SigmaY[n] ) - FunctionPhi((yMin - m_CenterY[n]) / m_SigmaY[n] );
summand2 = FunctionPhi((zMax - m_CenterZ[n]) / m_SigmaZ[n] ) - FunctionPhi((zMin - m_CenterZ[n]) / m_SigmaZ[n] );
value = summand0 * summand1 * summand2;
factor = (m_SigmaX[n] * m_SigmaY[n] * m_SigmaZ[n] ) * pow(2.0 * itk::Math::pi, 1.5 );
mean = mean + factor * value * m_Altitude[n];
}
return mean;
}
//---------------------------------------------------------------------------------------------------------------------
/*
Returns the linear interpolation of the values of the standard normal distribution function. This values could be seen in the vector m_NormalDistValues.
*/
template< class TOutputImage >
double
MultiGaussianImageSource< TOutputImage >
::FunctionPhi(double value)
{
double phiAtValue;
//linear interpolation between the values
int indexValue = static_cast<int>( 100 * value);
if( indexValue > 409 )
{
return phiAtValue = 1.0;
}
else if( indexValue < -409 )
{
return phiAtValue = 0.0;
}
else if( indexValue == 409 )
{
return phiAtValue = m_NormalDistValues[409];
}
else if( indexValue == -409 )
{
return phiAtValue = 1.0 - m_NormalDistValues[409];
}
else
{
bool negative = false;
if (indexValue < 0.0)
{
negative = true;
value = -value;
}
int indexUp = static_cast<int>( 100 * value) + 1;
int indexDown = static_cast<int>( 100 * value);
double alpha = 100.0 * value - static_cast<double>(indexDown);
phiAtValue = (1.0 - alpha) * m_NormalDistValues[indexDown] + alpha * m_NormalDistValues[indexUp] ;
if(negative)
{
phiAtValue = 1.0 - phiAtValue;
}
return phiAtValue;
}
}
//----------------------------------------------------------------------------------------------------------------------
/*
Set the midpoint of the cuboid in a vector m_Midpoints. This cuboids discretise the sphere with the octree method.
Set the radius of the cuboid ( = 0.5 * length of the side of the cuboid ) in the vector m_RadiusCuboid.
*/
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::InsertPoints( PointType globalCoordinateMidpointCuboid, double cuboidRadius)
{
typename MapContainerPoints::ElementIdentifier id = m_Midpoints.Size();
m_Midpoints.InsertElement(id, globalCoordinateMidpointCuboid);
m_RadiusCuboid.InsertElement(id, cuboidRadius);
}
//----------------------------------------------------------------------------------------------------------------------
/* This recursive method realise the octree method. It subdivide a cuboid in eight cuboids, when this cuboid crosses the boundary of sphere. If the cuboid is inside the sphere, it calculates the integral and, if uncommented, insert the midpoint of the cuboid in the m_Midpoints vector.
*/
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::CalculateEdgesInSphere( PointType globalCoordinateMidpointCuboid, PointType globalCoordinateMidpointSphere, double cuboidRadius, int level )
{
double xMin, xMax, yMin, yMax, zMin, zMax;
double cuboidRadiusRecursion = cuboidRadius;
PointType newMidpoint;
int intersect = IntersectTheSphere( globalCoordinateMidpointCuboid, globalCoordinateMidpointSphere, cuboidRadiusRecursion);
if( intersect == 1 )
{
if (level < 4)
{
// cuboid intersect the sphere -> call CalculateEdgesInSphere (this method) for the subdivided cuboid
cuboidRadiusRecursion = cuboidRadiusRecursion / 2.0;
for(int i = -1; i < 2; i+=2)
{
for(int k = -1; k < 2; k+=2)
{
for(int j = -1; j < 2; j+=2)
{
newMidpoint[0] = globalCoordinateMidpointCuboid[0] + static_cast<double>(i) * cuboidRadiusRecursion;
newMidpoint[1] = globalCoordinateMidpointCuboid[1] + static_cast<double>(k) * cuboidRadiusRecursion;
newMidpoint[2] = globalCoordinateMidpointCuboid[2] + static_cast<double>(j) * cuboidRadiusRecursion;
this->CalculateEdgesInSphere( newMidpoint, globalCoordinateMidpointSphere, cuboidRadiusRecursion, level + 1 );
}
}
}
}
// last step of recursion -> on the boundary
else
{
// Calculate the integral and take the half of it (because we are on the boundary)
xMin = globalCoordinateMidpointCuboid[0] - cuboidRadius;
xMax = globalCoordinateMidpointCuboid[0] + cuboidRadius;
yMin = globalCoordinateMidpointCuboid[1] - cuboidRadius;
yMax = globalCoordinateMidpointCuboid[1] + cuboidRadius;
zMin = globalCoordinateMidpointCuboid[2] - cuboidRadius;
zMax = globalCoordinateMidpointCuboid[2] + cuboidRadius;
// size is in index coordinate -> multiply by the spacing -> global coordinate of the image boundary
// yz Plane
bool yzPlaneNotCrossYSection = xMin >= m_Origin[0] && xMax <= m_Size[0] * m_Spacing[0];
// xz Plane
bool xzPlaneNotCrossYSection = yMin >= m_Origin[1] && yMax <= m_Size[1] * m_Spacing[1];
// xy Plane
bool xyPlaneNotCrossZSection = zMin >= m_Origin[2] && zMax <= m_Size[2] * m_Spacing[2];
//check if the boundary of the integral is inside the image
if( yzPlaneNotCrossYSection && xzPlaneNotCrossYSection && xyPlaneNotCrossZSection)
{
//double temp = this->MultiGaussianFunctionValueAtCuboid( xMin, xMax, yMin, yMax, zMin, zMax ) * 0.5;
m_meanValueTemp = m_meanValueTemp + this->MultiGaussianFunctionValueAtCuboid( xMin, xMax, yMin, yMax, zMin, zMax ) * 0.5;
m_Volume = m_Volume + pow( 2.0 * cuboidRadius, 3.0) / 2.0;
}
}
}
else if(intersect == 2)
{
// cuboid in the sphere
// To insert the midpoint and the radius of the cuboid in a vector, so that we can visualise the midpoints, uncomment the next line and the line WriteXMLToTestTheCuboidInsideTheSphere();
// InsertPoints(globalCoordinateMidpointCuboid, cuboidRadius);
// Calculate the integral boundary
xMin = globalCoordinateMidpointCuboid[0] - cuboidRadius;
xMax = globalCoordinateMidpointCuboid[0] + cuboidRadius;
yMin = globalCoordinateMidpointCuboid[1] - cuboidRadius;
yMax = globalCoordinateMidpointCuboid[1] + cuboidRadius;
zMin = globalCoordinateMidpointCuboid[2] - cuboidRadius;
zMax = globalCoordinateMidpointCuboid[2] + cuboidRadius;
// size is in index coordinate -> multiply by the spacing -> global coordinate of the image boundary
// yz Plane
// bool yzPlaneAtOriginCrossXSection = xMin <= m_Origin[0] && xMax >= m_Origin[0];
// bool yzPlaneAtImageBorderCrossXSection = xMin <= m_Size[0] * m_Spacing[0] && xMax >= m_Size[0] * m_Spacing[0];
bool yzPlaneNotCrossYSection = xMin >= m_Origin[0] && xMax <= m_Size[0] * m_Spacing[0];
// xz Plane
// bool xzPlaneAtOriginCrossYSection = yMin <= m_Origin[1] && yMax >= m_Origin[1];
// bool xzPlaneAtImageBorderCrossYSection = yMin <= m_Size[1] * m_Spacing[1] && yMax >= m_Size[1] * m_Spacing[1];
bool xzPlaneNotCrossYSection = yMin >= m_Origin[1] && yMax <= m_Size[1] * m_Spacing[1];
// xy Plane
// bool xyPlaneAtOriginCrossZSection = zMin <= m_Origin[2] && zMax >= m_Origin[2];
// bool xyPlaneAtImageBorderCrossZSection = zMin <= m_Size[2] * m_Spacing[2] && zMax >= m_Size[2] * m_Spacing[2];
bool xyPlaneNotCrossZSection = zMin >= m_Origin[2] && zMax <= m_Size[2] * m_Spacing[2];
if( yzPlaneNotCrossYSection && xzPlaneNotCrossYSection && xyPlaneNotCrossZSection)
{
m_meanValueTemp = m_meanValueTemp + this->MultiGaussianFunctionValueAtCuboid( xMin, xMax, yMin, yMax, zMin, zMax );
m_Volume = m_Volume + pow( 2.0 * cuboidRadius, 3.0);
}
// check if the boundary of the image intersect the cuboid and if yes, change the limits of the cuboid to be only inside the image; therefor we cut the sphere and neglect the part of it outside the image
else
/*
if( // one plane crosses the cuboid
( (yzPlaneAtOriginCrossXSection && xzPlaneNotCrossYSection && xyPlaneNotCrossZSection) ||
(yzPlaneAtImageBorderCrossXSection && xzPlaneNotCrossYSection && xyPlaneNotCrossZSection) ||
(yzPlaneNotCrossYSection && xzPlaneAtOriginCrossYSection && xyPlaneNotCrossZSection) ||
(yzPlaneNotCrossYSection && xzPlaneAtImageBorderCrossYSection && xyPlaneNotCrossZSection) ||
(yzPlaneNotCrossYSection && xzPlaneNotCrossYSection && xyPlaneAtOriginCrossZSection) ||
(yzPlaneNotCrossYSection && xzPlaneNotCrossYSection && xyPlaneAtImageBorderCrossZSection) )
|| // two plane cross the cuboid (on the image edges possible)
( (yzPlaneAtOriginCrossXSection && xzPlaneAtOriginCrossYSection && xyPlaneNotCrossZSection) ||
( (yzPlaneAtOriginCrossXSection && xzPlaneNotCrossYSection && xyPlaneAtOriginCrossZSection) ||
(yzPlaneAtImageBorderCrossXSection && xzPlaneAtImageBorderCrossYSection && xyPlaneNotCrossZSection) ||
(yzPlaneAtImageBorderCrossXSection && xzPlaneNotCrossYSection && xyPlaneAtImageBorderCrossZSection) ||
(yzPlaneNotCrossYSection && xzPlaneAtOriginCrossYSection && xyPlaneNotCrossZSection) ||
(yzPlaneNotCrossYSection && xzPlaneAtOriginCrossYSection && xyPlaneAtOriginCrossZSection) ||
(yzPlaneAtImageBorderCrossXSection && xzPlaneAtImageBorderCrossYSection && xyPlaneNotCrossZSection) ||
(yzPlaneNotCrossYSection && xzPlaneAtImageBorderCrossYSection && xyPlaneAtImageBorderCrossZSection) ||
(yzPlaneNotCrossYSection && xzPlaneAtImageBorderCrossYSection && xyPlaneAtOriginCrossZSection) ||
(yzPlaneAtOriginCrossXSection && xzPlaneNotCrossYSection && xyPlaneAtOriginCrossZSection) ||
(yzPlaneNotCrossYSection && xzPlaneAtImageBorderCrossYSection && xyPlaneAtImageBorderCrossZSection)) ||
(yzPlaneAtImageBorderCrossXSection && xzPlaneNotCrossYSection && xyPlaneAtImageBorderCrossZSection) )
)
*/
{
// x-Axis
if(xMin <= m_Origin[0] && xMax >= m_Origin[0])
{
xMin = m_Origin[0];
}else if(xMin <= m_Size[0] * m_Spacing[0] && xMax >= m_Size[0] * m_Spacing[0])
{
xMax = m_Size[0] * m_Spacing[0];
}
// y-Axis
if(yMin <= m_Origin[1] && yMax >= m_Origin[1])
{
yMin = m_Origin[1];
}else if(yMin <= m_Size[1] * m_Spacing[1] && yMax >= m_Size[1] * m_Spacing[1])
{
yMax = m_Size[1] * m_Spacing[1];
}
// z-Axis
if(zMin <= m_Origin[2] && zMax >= m_Origin[2])
{
zMin = m_Origin[2];
}else if(zMin <= m_Size[2] * m_Spacing[2] && zMax >= m_Size[2] * m_Spacing[2])
{
zMax = m_Size[2] * m_Spacing[2];
}
m_meanValueTemp = m_meanValueTemp + this->MultiGaussianFunctionValueAtCuboid( xMin, xMax, yMin, yMax, zMin, zMax );
m_Volume = m_Volume + (xMax - xMin) * (yMax - yMin) * (zMax - zMin) ;
}
}
}
//-----------------------------------------------------------------------------------------------------------------------
/* Start the octree recursion in eigth directions for the sphere with midpoint globalCoordinateMidpointSphere and, if uncommented, write this in a file, so that we can visualise it. */
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::GenerateCuboidSegmentationInSphere(PointType globalCoordinateMidpointSphere)
{
double cuboidRadius = m_Radius * 0.5;
PointType newMidpoint;
for(int i = -1; i < 2; i+=2)
{
for(int k = -1; k < 2; k+=2)
{
for(int j = -1; j < 2; j+=2)
{
newMidpoint[0] = globalCoordinateMidpointSphere[0] + static_cast<double>(i) * cuboidRadius;
newMidpoint[1] = globalCoordinateMidpointSphere[1] + static_cast<double>(k) * cuboidRadius;
newMidpoint[2] = globalCoordinateMidpointSphere[2] + static_cast<double>(j) * cuboidRadius;
CalculateEdgesInSphere( newMidpoint, globalCoordinateMidpointSphere, cuboidRadius, 0);
}
}
}
if(m_WriteMPS)
{
m_WriteMPS = 0;
// uncomment to write an .mps file to visualise the midpoints
// std::cout << "Wrote .xml to visualise the midpoints." << std::endl;
// WriteXMLToTestTheCuboidInsideTheSphere();
}
}
//----------------------------------------------------------------------------------------------------------------------
/* This class allows by the method CalculateTheMidpointAndTheMeanValueWithOctree() to find a sphere with a specified radius that has a maximal mean value over all sphere with that radius with midpoint inside or at the boundary of the image. We approximaze the sphere with the octree recursiv method.
CalculateTheMidpointAndTheMeanValueWithOctree works as follows:
1. The for-loops traverse the region of interest and assume the current point to be the wanted sphere midpoint.
2. Calculate the mean value for that sphere.
3. Compare with the until-now-found-maximum and take the bigger one. */
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::CalculateTheMidpointAndTheMeanValueWithOctree()
{
m_MeanValue = 0.0;
double meanValueTemp;
- PointType midpoint;
- //typename MapContainerPoints::ElementIdentifier cuboidNumber = m_Midpoints.Size();
- // SetNormalDistributionValues();
- //double radius;
- //double xMin, xMax, yMin, yMax, zMin, zMax;
m_WriteMPS = 1;
PointType globalCoordinateMidpointSphere;
IndexType index;
OutputImageRegionType regionOfInterest;
IndexType indexR;
indexR.SetElement( 0, m_RegionOfInterestMin[0] );
indexR.SetElement( 1, m_RegionOfInterestMin[1] );
indexR.SetElement( 2, m_RegionOfInterestMin[2] );
regionOfInterest.SetIndex(indexR);
SizeType sizeROI;
sizeROI.SetElement( 0, m_RegionOfInterestMax[0] - m_RegionOfInterestMin[0] + 1);
sizeROI.SetElement( 1, m_RegionOfInterestMax[1] - m_RegionOfInterestMin[1] + 1);
sizeROI.SetElement( 2, m_RegionOfInterestMax[2] - m_RegionOfInterestMin[2] + 1);
regionOfInterest.SetSize(sizeROI);
typename TOutputImage::Pointer image = this->GetOutput(0);
IteratorType regionOfInterestIterator(image, regionOfInterest);
for(regionOfInterestIterator.GoToBegin(); !regionOfInterestIterator.IsAtEnd(); ++regionOfInterestIterator)
{
index = regionOfInterestIterator.GetIndex();
image->TransformIndexToPhysicalPoint(index, globalCoordinateMidpointSphere);
m_Volume = 0.0;
m_meanValueTemp = 0.0;
this->GenerateCuboidSegmentationInSphere(globalCoordinateMidpointSphere);
meanValueTemp = m_meanValueTemp / m_Volume;
// std::cout << "index: " << index <<" meanvalue: " << meanValueTemp << std::endl;
if(meanValueTemp > m_MeanValue)
{
m_GlobalCoordinate = globalCoordinateMidpointSphere;
m_MeanValue = meanValueTemp;
m_SphereMidpoint = index;
}
}
}
//----------------------------------------------------------------------------------------------------------------------
/*
Check if a cuboid intersect the sphere boundary. Returns 2, if the cuboid is inside the sphere; returns 1, if the cuboid intersects the sphere boundary and 0, if the cuboid is out of the sphere.
*/
template< class TOutputImage >
unsigned int
MultiGaussianImageSource< TOutputImage >
::IntersectTheSphere( PointType globalCoordinateMidpointCuboid, PointType globalCoordinateMidpointSphere, double cuboidRadius)
{
unsigned int intersect = 1;
PointType cuboidEdge;
int count = 0;
for(int i = -1; i < 2; i+=2)
{
for(int k = -1; k < 2; k+=2)
{
for(int j = -1; j < 2; j+=2)
{
cuboidEdge[0] = globalCoordinateMidpointCuboid[0] + static_cast<double>(i) * cuboidRadius;
cuboidEdge[1] = globalCoordinateMidpointCuboid[1] + static_cast<double>(k) * cuboidRadius;
cuboidEdge[2] = globalCoordinateMidpointCuboid[2] + static_cast<double>(j) * cuboidRadius;
if (globalCoordinateMidpointSphere.SquaredEuclideanDistanceTo(cuboidEdge) <= m_Radius * m_Radius)
{
++count;
}
}
}
}
if ( count == 0 )
{
// cuboid not in the sphere
intersect = 0;
}
if (count == 8 )
{
// cuboid in the sphere
intersect = 2;
}
return intersect;
}
//----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
double
MultiGaussianImageSource< TOutputImage >
::MultiGaussianFunctionValueAtPoint(double x, double y, double z)
{
//this claculate the mean value in the voxel
//integrate over the voxel with midpoint [x, y, z]
double summand0, summand1, summand2/*, power*/, value = 0.0, factor;
double xMin, xMax, yMin, yMax, zMin, zMax, mean;
mean = 0.0;
// the for-loop represent the sum of the gaussian function
xMin = x - m_Spacing[0] / 2.0;
xMax = x + m_Spacing[0] / 2.0;
yMin = y - m_Spacing[1] / 2.0;
yMax = y + m_Spacing[1] / 2.0;
zMin = z - m_Spacing[2] / 2.0;
zMax = z + m_Spacing[2] / 2.0;
for( unsigned int n = 0; n < m_NumberOfGaussians; ++n )
{
summand0 = FunctionPhi( (xMax - m_CenterX[n]) / m_SigmaX[n] ) - FunctionPhi( (xMin - m_CenterX[n]) / m_SigmaX[n] );
summand1 = FunctionPhi( (yMax - m_CenterY[n]) / m_SigmaY[n] ) - FunctionPhi( (yMin - m_CenterY[n]) / m_SigmaY[n] );
summand2 = FunctionPhi( (zMax - m_CenterZ[n]) / m_SigmaZ[n] ) - FunctionPhi( (zMin - m_CenterZ[n]) / m_SigmaZ[n] );
value = summand0 * summand1 * summand2;
factor = ( m_SigmaX[n] * m_SigmaY[n] * m_SigmaZ[n] ) * pow( 2.0 * itk::Math::pi, 1.5 );
mean = mean + factor * value * m_Altitude[n];
}
value = mean / (m_Spacing[0] * m_Spacing[1] * m_Spacing[2] );
/*
//this calculate the value of the gaussian at the midpoint of the voxel:
double summand0, summand1, summand2, power, value = 0.0;
// the for-loop represent the sum of the gaussian function
for(unsigned int n =0; n < m_NumberOfGaussians; ++n)
{
summand0 = ( x - m_CenterX[n] ) / m_SigmaX[n];
summand1 = ( y - m_CenterY[n] ) / m_SigmaY[n];
summand2 = ( z - m_CenterZ[n] ) / m_SigmaZ[n];
power = summand0 * summand0 + summand1 * summand1 + summand2 * summand2;
value = value + m_Altitude[n] * pow(itk::Math::e, -0.5 * power);
}
*/
// std::cout << "X: " << xMin << " " << x << " "<< xMax << " Y: "<< yMin << " " << y << " " << yMax << " Z: "<< zMin << " "<< z << " " << zMax << " value: " << value << std::endl;
return value;
}
//----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::AddGaussian( VectorType x, VectorType y, VectorType z, VectorType sx, VectorType sy, VectorType sz, VectorType altitude)
{
for(unsigned int i = 0; i < x.size(); ++i)
{
m_CenterX.push_back( x[i] );
m_CenterY.push_back( y[i] );
m_CenterZ.push_back( z[i] );
m_SigmaX.push_back( sx[i] );
m_SigmaY.push_back( sy[i] );
m_SigmaZ.push_back( sz[i] );
m_Altitude.push_back(altitude[i]);
}
}
//-----------------------------------------------------------------------------------------------------------------------
template< typename TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::GenerateOutputInformation()
{
TOutputImage *output;
IndexType index;
index.Fill(0);
output = this->GetOutput(0);
typename TOutputImage::RegionType largestPossibleRegion;
largestPossibleRegion.SetSize(this->m_Size);
largestPossibleRegion.SetIndex(index);
output->SetLargestPossibleRegion(largestPossibleRegion);
output->SetSpacing(m_Spacing);
output->SetOrigin(m_Origin);
}
//-----------------------------------------------------------------------------------------------------------------------
template< typename TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::GenerateData()
{
itkDebugMacro(<< "Generating a image of scalars ");
double valueReal;
IndexType index;
typename TOutputImage::Pointer image = this->GetOutput(0);
image = this->GetOutput(0);
image->SetBufferedRegion( image->GetRequestedRegion() );
image->Allocate();
IteratorType imageIt(image, image->GetLargestPossibleRegion());
PointType globalCoordinate;
this->SetNormalDistributionValues();
for(imageIt.GoToBegin(); !imageIt.IsAtEnd(); ++imageIt)
{
valueReal = 0.0;
index = imageIt.GetIndex();
image->TransformIndexToPhysicalPoint(imageIt.GetIndex(), globalCoordinate);
valueReal = MultiGaussianFunctionValueAtPoint(globalCoordinate[0], globalCoordinate[1] ,globalCoordinate[2]);
imageIt.Set(valueReal);
}
}
//-----------------------------------------------------------------------------------------------------------------------
/*This method is used to write a .mps file, so that we can visualize the midpoints of the approximated sphere as a scatterplot (for example with MITK Workbench).
*/
template< typename TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::WriteXMLToTestTheCuboidInsideTheSphere()
{
std::stringstream ss;
int numberSummand = 1.0;
//write an .mps test file
itk::DOMNodeXMLWriter::Pointer xmlWriter;
typedef itk::DOMNode::Pointer DOMNodeType;
DOMNodeType domXML, domPointSetFile, domFileVersion, domPointSet, domPoint, domId, domX, domY, domZ;
xmlWriter = itk::DOMNodeXMLWriter::New();
domXML = itk::DOMNode::New();
domXML->SetName("?xml");
domPointSetFile = itk::DOMNode::New();
domPointSetFile->SetName("point_set_file");
//domFileVersion = itk::DOMNode::New();
//domFileVersion->SetName("file_version");
domPointSet = itk::DOMNode::New();
domPointSet->SetName("point_set");
ss.str("");
ss << 1.0;
domXML->SetAttribute("version", ss.str());
domXML->AddChildAtBegin(domPointSetFile);
//domPointSetFile -> AddChildAtBegin(domFileVersion);
domPointSetFile -> AddChildAtBegin(domPointSet);
unsigned int cap = m_Midpoints.Size();
for(unsigned int iter = 0 ; iter < cap; ++iter)
{
domPoint = itk::DOMNode::New();
domPoint->SetName("point");
domX = itk::DOMNode::New();
domX->SetName("x");
domY = itk::DOMNode::New();
domY->SetName("y");
domZ = itk::DOMNode::New();
domZ->SetName("z");
domId = itk::DOMNode::New();
domId->SetName("id");
ss.str("");
ss << numberSummand;
domId->AddTextChildAtBegin(ss.str());
domPoint -> AddChildAtEnd(domId);
double scaleFactor = 10.0;
PointType point = m_Midpoints.GetElement( numberSummand - 1 );
ss.str("");
ss << point[0] * scaleFactor;
domX->AddTextChildAtBegin(ss.str());
domPoint -> AddChildAtEnd(domX);
ss.str("");
ss << point[1] * scaleFactor;
domY->AddTextChildAtBegin(ss.str());
domPoint -> AddChildAtEnd(domY);
ss.str("");
ss << point[2] * scaleFactor;
domZ->AddTextChildAtBegin(ss.str());
domPoint -> AddChildAtEnd(domZ);
domPointSet -> AddChildAtEnd(domPoint);
numberSummand += 1.0;
}
// .mps (Data)
ss.str("");
ss << "C:/temp/CuboidsInTheSphere.mps";
std::string name = ss.str();
char * fileNamePointer = (char*) name.c_str();
xmlWriter->SetFileName( fileNamePointer);
xmlWriter->SetInput( domXML );
xmlWriter->Update();
}
//-----------------------------------------------------------------------------------------------------------------------
template< typename TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::CalculateMaxAndMinInSphere()
{
IndexType index;
typename MultiGaussianImageSource< TOutputImage >::OutputImagePixelType value;
m_MaxValueInSphere = std::numeric_limits<OutputImagePixelType>::min();
m_MinValueInSphere = std::numeric_limits<OutputImagePixelType>::max();
int radInt, sizeRegion;
OutputImageRegionType cuboidRegion;
IndexType indexR;
SizeType sizeR;
int indexRegion, originAsIndex;
for( unsigned int i = 0; i < 3; ++i )
{
radInt = static_cast<int>(m_Radius/m_Spacing[i]);
indexRegion = m_SphereMidpoint[i] - radInt;
originAsIndex = static_cast<int>(m_Origin[i]/m_Spacing[i]);
if( originAsIndex > indexRegion )
{
indexR.SetElement(i, originAsIndex );
}
else
{
indexR.SetElement(i, indexRegion );
}
sizeRegion = 2 *radInt + 1;
int sizeOutputImage = m_Size[i];
if( (indexR[i] + sizeRegion) > (originAsIndex + sizeOutputImage) )
{
std::cout << "Not the entire sphere is in the image!" << std::endl;
sizeR.SetElement(i, m_Size[i] - indexRegion );
}
else
{
sizeR.SetElement(i, sizeRegion );
}
}
cuboidRegion.SetIndex(indexR);
cuboidRegion.SetSize(sizeR);
typename TOutputImage::Pointer image = this->GetOutput(0);
IteratorType cuboidRegionOfInterestIterator(image, cuboidRegion);
PointType globalCoordinate;
for(cuboidRegionOfInterestIterator.GoToBegin(); !cuboidRegionOfInterestIterator.IsAtEnd(); ++cuboidRegionOfInterestIterator)
{
index = cuboidRegionOfInterestIterator.GetIndex();
if(IsInImage(index))
{
image->TransformIndexToPhysicalPoint(cuboidRegionOfInterestIterator.GetIndex(), globalCoordinate);
if( m_GlobalCoordinate.EuclideanDistanceTo(globalCoordinate) <= m_Radius )
{
value = cuboidRegionOfInterestIterator.Get();
if(m_MaxValueInSphere < value)
{
m_MaxValueInSphere = value;
m_MaxValueIndexInSphere = index;
}
if(m_MinValueInSphere > value)
{
m_MinValueInSphere = value;
m_MinValueIndexInSphere = index;
}
}
}
}
}
//-----------------------------------------------------------------------------------------------------------------------
template< typename TOutputImage >
bool
MultiGaussianImageSource< TOutputImage >
::IsInImage(IndexType index)
{
bool isInImage = true;
int originAsIndex;
for( unsigned int i = 0; i < 3; ++i )
{
originAsIndex = static_cast<int>(m_Origin[i]/m_Spacing[i]);
int sizeOfOutputImage = m_Size[i];
if( index[i] < originAsIndex || index[i] > (originAsIndex + sizeOfOutputImage) )
return false;
}
return isInImage;
}
//-----------------------------------------------------------------------------------------------------------------------
template< typename TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::SetNormalDistributionValues()
{
double temp[410] = { 0.5 , 0.50399 , 0.50798, 0.51197, 0.51595, 0.51994, 0.52392, 0.5279, 0.53188, 0.53586, 0.53983, 0.5438, 0.54776, 0.55172, 0.55567, 0.55962, 0.56356, 0.56749, 0.57142, 0.57535, 0.57926, 0.58317, 0.58706, 0.59095, 0.59483 , 0.59871, 0.60257, 0.60642, 0.61026, 0.61409, 0.61791, 0.62172, 0.62552, 0.6293, 0.63307, 0.63683, 0.64058, 0.64431, 0.64803, 0.65173, 0.65542, 0.6591, 0.66276, 0.6664, 0.67003, 0.67364, 0.67724, 0.68082, 0.68439, 0.68793, 0.69146, 0.69497, 0.69847, 0.70194, 0.7054, 0.70884, 0.71226, 0.71566, 0.71904, 0.7224, 0.72575, 0.72907, 0.73237, 0.73565, 0.73891, 0.74215, 0.74537, 0.74857, 0.75175, 0.7549, 0.75804, 0.76115, 0.76424, 0.7673, 0.77035, 0.77337, 0.77637, 0.77935, 0.7823, 0.78524, 0.78814, 0.79103, 0.79389, 0.79673, 0.79955, 0.80234, 0.80511, 0.80785, 0.81057, 0.81327, 0.81594, 0.81859, 0.82121, 0.82381, 0.82639, 0.82894, 0.83147, 0.83398, 0.83646, 0.83891, 0.84134, 0.84375, 0.84614, 0.84849, 0.85083, 0.85314, 0.85543, 0.85769, 0.85993, 0.86214, 0.86433, 0.8665, 0.86864, 0.87076, 0.87286, 0.87493, 0.87698, 0.879, 0.881, 0.88298, 0.88493, 0.88686, 0.88877, 0.89065, 0.89251, 0.89435, 0.89617, 0.89796, 0.89973, 0.90147, 0.9032, 0.9049, 0.90658, 0.90824, 0.90988, 0.91149, 0.91309, 0.91466, 0.91621, 0.91774, 0.91924, 0.92073, 0.9222, 0.92364, 0.92507, 0.92647, 0.92785, 0.92922, 0.93056, 0.93189, 0.93319, 0.93448, 0.93574, 0.93699, 0.93822, 0.93943, 0.94062, 0.94179, 0.94295, 0.94408, 0.9452, 0.9463, 0.94738, 0.94845, 0.9495, 0.95053, 0.95154, 0.95254, 0.95352, 0.95449, 0.95543, 0.95637, 0.95728, 0.95818, 0.95907, 0.95994, 0.9608, 0.96164, 0.96246, 0.96327, 0.96407, 0.96485, 0.96562, 0.96638, 0.96712, 0.96784, 0.96856, 0.96926, 0.96995, 0.97062, 0.97128, 0.97193, 0.97257, 0.9732, 0.97381, 0.97441, 0.975, 0.97558, 0.97615, 0.9767, 0.97725, 0.97778, 0.97831, 0.97882, 0.97932, 0.97982, 0.9803, 0.98077, 0.98124, 0.98169, 0.98214, 0.98257, 0.983, 0.98341, 0.98382, 0.98422, 0.98461, 0.985, 0.98537, 0.98574, 0.9861, 0.98645, 0.98679, 0.98713, 0.98745, 0.98778, 0.98809, 0.9884, 0.9887, 0.98899, 0.98928, 0.98956, 0.98983, 0.9901, 0.99036, 0.99061, 0.99086, 0.99111, 0.99134, 0.99158, 0.9918, 0.99202, 0.99224, 0.99245, 0.99266, 0.99286, 0.99305, 0.99324, 0.99343, 0.99361, 0.99379, 0.99396, 0.99413, 0.9943, 0.99446, 0.99461, 0.99477, 0.99492, 0.99506, 0.9952, 0.99534, 0.99547, 0.9956, 0.99573, 0.99585, 0.99598, 0.99609, 0.99621, 0.99632, 0.99643, 0.99653, 0.99664, 0.99674, 0.99683, 0.99693, 0.99702, 0.99711, 0.9972, 0.99728, 0.99736, 0.99744, 0.99752, 0.9976, 0.99767, 0.99774, 0.99781, 0.99788, 0.99795, 0.99801, 0.99807, 0.99813, 0.99819, 0.99825, 0.99831, 0.99836, 0.99841, 0.99846, 0.99851, 0.99856, 0.99861, 0.99865, 0.99869, 0.99874, 0.99878, 0.99882, 0.99886, 0.99889, 0.99893, 0.99896, 0.999, 0.99903, 0.99906, 0.9991, 0.99913, 0.99916, 0.99918, 0.99921, 0.99924, 0.99926, 0.99929, 0.99931, 0.99934, 0.99936, 0.99938, 0.9994, 0.99942, 0.99944, 0.99946, 0.99948, 0.9995, 0.99952, 0.99953, 0.99955, 0.99957, 0.99958, 0.9996, 0.99961, 0.99962, 0.99964, 0.99965, 0.99966, 0.99968, 0.99969, 0.9997, 0.99971, 0.99972, 0.99973, 0.99974, 0.99975, 0.99976, 0.99977, 0.99978, 0.99978, 0.99979, 0.9998, 0.99981, 0.99981, 0.99982, 0.99983, 0.99983, 0.99984, 0.99985, 0.99985, 0.99986, 0.99986, 0.99987, 0.99987, 0.99988, 0.99988, 0.99989, 0.99989, 0.9999, 0.9999, 0.9999, 0.99991, 0.99991, 0.99992, 0.99992, 0.99992, 0.99992, 0.99993, 0.99993, 0.99993, 0.99994, 0.99994, 0.99994, 0.99994, 0.99995, 0.99995, 0.99995, 0.99995, 0.99995, 0.99996, 0.99996, 0.99996, 0.99996, 0.99996, 0.99996, 0.99997, 0.99997, 0.99997, 0.99997, 0.99997, 0.99997, 0.99997, 0.99997, 0.99998, 0.99998, 0.99998, 0.99998 };
for(int i=0; i < 410; i++)
{
m_NormalDistValues[i] = temp[i];
}
}
} // end namespace itk
#endif
diff --git a/Modules/ImageStatistics/mitkExtendedLabelStatisticsImageFilter.h b/Modules/ImageStatistics/mitkExtendedLabelStatisticsImageFilter.h
deleted file mode 100644
index 37352663b7..0000000000
--- a/Modules/ImageStatistics/mitkExtendedLabelStatisticsImageFilter.h
+++ /dev/null
@@ -1,347 +0,0 @@
-/*============================================================================
-
-The Medical Imaging Interaction Toolkit (MITK)
-
-Copyright (c) German Cancer Research Center (DKFZ)
-All rights reserved.
-
-Use of this source code is governed by a 3-clause BSD license that can be
-found in the LICENSE file.
-
-============================================================================*/
-#ifndef __mitkExtendedLabelStatisticsImageFilter
-#define __mitkExtendedLabelStatisticsImageFilter
-
-#include "itkLabelStatisticsImageFilter.h"
-
-namespace itk
-{
- /**
- * \class ExtendedLabelStatisticsImageFilter
- * \brief Extension of the itkLabelStatisticsImageFilter that also calculates the Skewness,Kurtosis,Entropy,Uniformity.
- *
- * This class inherits from the itkLabelStatisticsImageFilter and
- * uses its results for the calculation of seven additional coefficients:
- * the Skewness, Kurtosis, Uniformity, UPP, MPP, Entropy and Median
- *
- *
- */
- template< class TInputImage, class TLabelImage >
- class ExtendedLabelStatisticsImageFilter : public LabelStatisticsImageFilter< TInputImage, TLabelImage >
- {
- public:
-
- typedef ExtendedLabelStatisticsImageFilter Self;
- typedef LabelStatisticsImageFilter < TInputImage, TLabelImage > Superclass;
- typedef SmartPointer< Self > Pointer;
- typedef SmartPointer< const Self > ConstPointer;
- typedef typename Superclass::LabelPixelType LabelPixelType;
- typedef typename Superclass::RealType RealType;
- typedef typename Superclass::PixelType PixelType;
- typedef typename Superclass::MapIterator MapIterator;
- typedef typename Superclass::BoundingBoxType BoundingBoxType;
- typedef typename Superclass::RegionType RegionType;
- typedef itk::Statistics::Histogram<double> HistogramType;
-
- itkFactorylessNewMacro( Self );
- itkCloneMacro( Self );
- itkTypeMacro(ExtendedLabelStatisticsImageFilter, LabelStatisticsImageFilter);
-
-
- /** \class LabelStatistics
- * \brief Statistics stored per label
- * \ingroup ITKImageStatistics
- */
- class LabelStatistics
- {
- public:
-
- // default constructor
- LabelStatistics()
- {
- // initialized to the default values
- m_Count = NumericTraits< IdentifierType >::ZeroValue();
- m_PositivePixelCount = NumericTraits< IdentifierType >::ZeroValue();
- m_Sum = NumericTraits< RealType >::ZeroValue();
- m_SumOfPositivePixels = NumericTraits< RealType >::ZeroValue();
-
- m_SumOfSquares = NumericTraits< RealType >::ZeroValue();
- m_SumOfCubes = NumericTraits< RealType >::ZeroValue();
- m_SumOfQuadruples = NumericTraits< RealType >::ZeroValue();
-
- // Set such that the first pixel encountered can be compared
- m_Minimum = NumericTraits< RealType >::max();
- m_Maximum = NumericTraits< RealType >::NonpositiveMin();
-
- // Default these to zero
- m_Mean = NumericTraits< RealType >::ZeroValue();
- m_Sigma = NumericTraits< RealType >::ZeroValue();
- m_Variance = NumericTraits< RealType >::ZeroValue();
- m_MPP = NumericTraits< RealType >::ZeroValue();
- m_Median = NumericTraits< RealType >::ZeroValue();
- m_Uniformity = NumericTraits< RealType >::ZeroValue();
- m_UPP = NumericTraits< RealType >::ZeroValue();
- m_Entropy = NumericTraits< RealType >::ZeroValue();
- m_Skewness = NumericTraits< RealType >::ZeroValue();
- m_Kurtosis = NumericTraits< RealType >::ZeroValue();
-
- unsigned int imageDimension = itkGetStaticConstMacro(ImageDimension);
- m_BoundingBox.resize(imageDimension * 2);
- for ( unsigned int i = 0; i < imageDimension * 2; i += 2 )
- {
- m_BoundingBox[i] = NumericTraits< IndexValueType >::max();
- m_BoundingBox[i + 1] = NumericTraits< IndexValueType >::NonpositiveMin();
- }
- m_Histogram = nullptr;
- }
-
- // constructor with histogram enabled
- LabelStatistics(int size, RealType lowerBound, RealType upperBound)
- {
- // initialized to the default values
- m_Count = NumericTraits< IdentifierType >::ZeroValue();
- m_PositivePixelCount = NumericTraits< IdentifierType >::ZeroValue();
- m_Sum = NumericTraits< RealType >::ZeroValue();
- m_SumOfPositivePixels = NumericTraits< RealType >::ZeroValue();
-
- m_SumOfSquares = NumericTraits< RealType >::ZeroValue();
- m_SumOfCubes = NumericTraits< RealType >::ZeroValue();
- m_SumOfQuadruples = NumericTraits< RealType >::ZeroValue();
-
- // Set such that the first pixel encountered can be compared
- m_Minimum = NumericTraits< RealType >::max();
- m_Maximum = NumericTraits< RealType >::NonpositiveMin();
-
- // Default these to zero
- m_Mean = NumericTraits< RealType >::ZeroValue();
- m_Sigma = NumericTraits< RealType >::ZeroValue();
- m_Variance = NumericTraits< RealType >::ZeroValue();
- m_MPP = NumericTraits< RealType >::ZeroValue();
- m_Median = NumericTraits< RealType >::ZeroValue();
- m_Uniformity = NumericTraits< RealType >::ZeroValue();
- m_UPP = NumericTraits< RealType >::ZeroValue();
- m_Entropy = NumericTraits< RealType >::ZeroValue();
- m_Skewness = NumericTraits< RealType >::ZeroValue();
- m_Kurtosis = NumericTraits< RealType >::ZeroValue();
-
-
- unsigned int imageDimension = itkGetStaticConstMacro(ImageDimension);
- m_BoundingBox.resize(imageDimension * 2);
- for ( unsigned int i = 0; i < imageDimension * 2; i += 2 )
- {
- m_BoundingBox[i] = NumericTraits< IndexValueType >::max();
- m_BoundingBox[i + 1] = NumericTraits< IndexValueType >::NonpositiveMin();
- }
-
- // Histogram
- m_Histogram = HistogramType::New();
- typename HistogramType::SizeType hsize;
- typename HistogramType::MeasurementVectorType lb;
- typename HistogramType::MeasurementVectorType ub;
- hsize.SetSize(1);
- lb.SetSize(1);
- ub.SetSize(1);
- m_Histogram->SetMeasurementVectorSize(1);
- hsize[0] = size;
- lb[0] = lowerBound;
- ub[0] = upperBound;
- m_Histogram->Initialize(hsize, lb, ub);
- }
-
- // need copy constructor because of smart pointer to histogram
- LabelStatistics(const LabelStatistics & l)
- {
- m_Count = l.m_Count;
- m_Minimum = l.m_Minimum;
- m_Maximum = l.m_Maximum;
- m_Mean = l.m_Mean;
- m_Sum = l.m_Sum;
- m_SumOfSquares = l.m_SumOfSquares;
- m_Sigma = l.m_Sigma;
- m_Variance = l.m_Variance;
- m_MPP = l.m_MPP;
- m_Median = l.m_Median;
- m_Uniformity = l.m_Uniformity;
- m_UPP = l.m_UPP;
- m_Entropy = l.m_Entropy;
- m_Skewness = l.m_Skewness;
- m_Kurtosis = l.m_Kurtosis;
- m_BoundingBox = l.m_BoundingBox;
- m_Histogram = l.m_Histogram;
- m_SumOfPositivePixels = l.m_SumOfPositivePixels;
- m_PositivePixelCount = l.m_PositivePixelCount;
- m_SumOfCubes = l.m_SumOfCubes;
- m_SumOfQuadruples = l.m_SumOfQuadruples;
- }
-
- // added for completeness
- LabelStatistics &operator= (const LabelStatistics& l)
- {
- if(this != &l)
- {
- m_Count = l.m_Count;
- m_Minimum = l.m_Minimum;
- m_Maximum = l.m_Maximum;
- m_Mean = l.m_Mean;
- m_Sum = l.m_Sum;
- m_SumOfSquares = l.m_SumOfSquares;
- m_Sigma = l.m_Sigma;
- m_Variance = l.m_Variance;
- m_MPP = l.m_MPP;
- m_Median = l.m_Median;
- m_Uniformity = l.m_Uniformity;
- m_UPP = l.m_UPP;
- m_Entropy = l.m_Entropy;
- m_Skewness = l.m_Skewness;
- m_Kurtosis = l.m_Kurtosis;
- m_BoundingBox = l.m_BoundingBox;
- m_Histogram = l.m_Histogram;
- m_SumOfPositivePixels = l.m_SumOfPositivePixels;
- m_PositivePixelCount = l.m_PositivePixelCount;
- m_SumOfCubes = l.m_SumOfCubes;
- m_SumOfQuadruples = l.m_SumOfQuadruples;
- }
- return *this;
- }
-
- IdentifierType m_Count;
- RealType m_Minimum;
- RealType m_Maximum;
- RealType m_Mean;
- RealType m_Sum;
- RealType m_SumOfSquares;
- RealType m_Sigma;
- RealType m_Variance;
- RealType m_MPP;
- RealType m_Median;
- RealType m_Uniformity;
- RealType m_UPP;
- RealType m_Entropy;
- RealType m_Skewness;
- RealType m_Kurtosis;
- IdentifierType m_PositivePixelCount;
- RealType m_SumOfPositivePixels;
- RealType m_SumOfCubes;
- RealType m_SumOfQuadruples;
- typename Superclass::BoundingBoxType m_BoundingBox;
- typename HistogramType::Pointer m_Histogram;
- };
-
- /** Type of the map used to store data per label */
- typedef itksys::hash_map< LabelPixelType, LabelStatistics > MapType;
- typedef typename itksys::hash_map< LabelPixelType, LabelStatistics >::const_iterator StatisticsMapConstIterator;
- typedef typename itksys::hash_map< LabelPixelType, LabelStatistics >::iterator StatisticsMapIterator;
- typedef IdentifierType MapSizeType;
-
- /** Type of the container used to store valid label values */
- typedef std::vector<LabelPixelType> ValidLabelValuesContainerType;
-
- /** Return the computed Minimum for a label. */
- RealType GetMinimum(LabelPixelType label) const;
-
- /** Return the computed Maximum for a label. */
- RealType GetMaximum(LabelPixelType label) const;
-
- /** Return the computed Mean for a label. */
- RealType GetMean(LabelPixelType label) const;
-
- /** Return the computed Standard Deviation for a label. */
- RealType GetSigma(LabelPixelType label) const;
-
- /** Return the computed Variance for a label. */
- RealType GetVariance(LabelPixelType label) const;
-
- /** Return the computed bounding box for a label. */
- BoundingBoxType GetBoundingBox(LabelPixelType label) const;
-
- /** Return the computed region. */
- RegionType GetRegion(LabelPixelType label) const;
-
- /** Return the compute Sum for a label. */
- RealType GetSum(LabelPixelType label) const;
-
- /** Return the number of pixels for a label. */
- MapSizeType GetCount(LabelPixelType label) const;
-
- /** Return the histogram for a label */
- HistogramType::Pointer GetHistogram(LabelPixelType label) const;
-
- /*getter method for the new statistics*/
- RealType GetSkewness(LabelPixelType label) const;
- RealType GetKurtosis(LabelPixelType label) const;
- RealType GetUniformity( LabelPixelType label) const;
- RealType GetMedian( LabelPixelType label) const;
- RealType GetEntropy( LabelPixelType label) const;
- RealType GetMPP( LabelPixelType label) const;
- RealType GetUPP( LabelPixelType label) const;
-
- bool GetMaskingNonEmpty() const;
-
- std::list<int> GetRelevantLabels() const;
-
-
- /** specify global Histogram parameters. If the histogram parameters are set with this function, the same min and max value are used for all histograms. */
- void SetHistogramParameters(const int numBins, RealType lowerBound,
- RealType upperBound);
-
- /** specify Histogram parameters for each label individually. Labels in the label image that are not represented in the std::maps here will receive global parameters (if available) */
- void SetHistogramParametersForLabels(std::map<LabelPixelType, unsigned int> numBins, std::map<LabelPixelType, PixelType> lowerBound,
- std::map<LabelPixelType, PixelType> upperBound);
-
- protected:
- ExtendedLabelStatisticsImageFilter():
- m_GlobalHistogramParametersSet(false),
- m_MaskNonEmpty(false),
- m_LabelHistogramParametersSet(false),
- m_PreferGlobalHistogramParameters(false)
- {
- m_NumBins.set_size(1);
- }
-
- ~ExtendedLabelStatisticsImageFilter() override{}
-
- void AfterThreadedGenerateData() override;
-
- /** Initialize some accumulators before the threads run. */
- void BeforeThreadedGenerateData() override;
-
- /** Multi-thread version GenerateData. */
- void ThreadedGenerateData(const typename TInputImage::RegionType &
- outputRegionForThread,
- ThreadIdType threadId) override;
-
- /** Does the specified label exist? Can only be called after a call
- * a call to Update(). */
- bool HasLabel(LabelPixelType label) const
- {
- return m_LabelStatistics.find(label) != m_LabelStatistics.end();
- }
-
- private:
- std::vector< MapType > m_LabelStatisticsPerThread;
- MapType m_LabelStatistics;
- ValidLabelValuesContainerType m_ValidLabelValues;
-
- bool m_GlobalHistogramParametersSet;
-
- typename HistogramType::SizeType m_NumBins;
-
- RealType m_LowerBound;
- RealType m_UpperBound;
-
- bool m_MaskNonEmpty;
-
- bool m_LabelHistogramParametersSet;
- std::map<LabelPixelType, PixelType> m_LabelMin, m_LabelMax;
- std::map<LabelPixelType, unsigned int> m_LabelNBins;
- bool m_PreferGlobalHistogramParameters;
-
- }; // end of class
-
-} // end namespace itk
-
-#ifndef ITK_MANUAL_INSTANTIATION
-#include "mitkExtendedLabelStatisticsImageFilter.hxx"
-#endif
-
-#endif
diff --git a/Modules/ImageStatistics/mitkExtendedLabelStatisticsImageFilter.hxx b/Modules/ImageStatistics/mitkExtendedLabelStatisticsImageFilter.hxx
deleted file mode 100644
index 05223f2105..0000000000
--- a/Modules/ImageStatistics/mitkExtendedLabelStatisticsImageFilter.hxx
+++ /dev/null
@@ -1,737 +0,0 @@
-/*============================================================================
-
-The Medical Imaging Interaction Toolkit (MITK)
-
-Copyright (c) German Cancer Research Center (DKFZ)
-All rights reserved.
-
-Use of this source code is governed by a 3-clause BSD license that can be
-found in the LICENSE file.
-
-============================================================================*/
-#ifndef _mitkExtendedLabelStatisticsImageFilter_hxx
-#define _mitkExtendedLabelStatisticsImageFilter_hxx
-
-#include "mitkExtendedLabelStatisticsImageFilter.h"
-
-#include "itkImageRegionConstIteratorWithIndex.h"
-#include "itkImageRegionConstIterator.h"
-#include <mbilog.h>
-#include <mitkLogMacros.h>
-#include "mitkNumericConstants.h"
-#include "mitkLogMacros.h"
-#include <mitkHistogramStatisticsCalculator.h>
-
-namespace itk
-{
- template< class TInputImage, class TLabelImage >
- bool
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::GetMaskingNonEmpty() const
- {
- return m_MaskNonEmpty;
- }
-
- template< typename TInputImage, typename TLabelImage >
- void
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::SetHistogramParameters(const int numBins, RealType lowerBound, RealType upperBound)
- {
- m_NumBins[0] = numBins;
- m_LowerBound = lowerBound;
- m_UpperBound = upperBound;
- m_GlobalHistogramParametersSet = true;
- m_PreferGlobalHistogramParameters = true;
- this->Modified();
- }
-
- template< typename TInputImage, typename TLabelImage >
- void
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::SetHistogramParametersForLabels(std::map<LabelPixelType, unsigned int> numBins, std::map<LabelPixelType, PixelType> lowerBound,
- std::map<LabelPixelType, PixelType> upperBound)
- {
- m_LabelMin = lowerBound;
- m_LabelMax = upperBound;
- m_LabelNBins = numBins;
- m_LabelHistogramParametersSet = true;
- m_PreferGlobalHistogramParameters = false;
- this->Modified();
- }
-
- template< class TInputImage, class TLabelImage >
- typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::GetUniformity(LabelPixelType label) const
- {
- StatisticsMapConstIterator mapIt;
-
- mapIt = m_LabelStatistics.find(label);
- if ( mapIt == m_LabelStatistics.end() )
- {
- mitkThrow() << "Label does not exist";
- }
- else
- {
- return ( *mapIt ).second.m_Uniformity;
- }
- }
-
-
- template< class TInputImage, class TLabelImage >
- typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::GetMedian(LabelPixelType label) const
- {
- StatisticsMapConstIterator mapIt;
-
- mapIt = m_LabelStatistics.find(label);
- if ( mapIt == m_LabelStatistics.end() )
- {
- mitkThrow() << "Label does not exist";
- }
- else
- {
- return ( *mapIt ).second.m_Median;
- }
- }
-
- template< class TInputImage, class TLabelImage >
- typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::GetEntropy(LabelPixelType label) const
- {
- StatisticsMapConstIterator mapIt;
-
- mapIt = m_LabelStatistics.find(label);
- if ( mapIt == m_LabelStatistics.end() )
- {
- mitkThrow() << "Label does not exist";
- }
- else
- {
- return ( *mapIt ).second.m_Entropy;
- }
- }
-
- template< class TInputImage, class TLabelImage >
- typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::GetUPP(LabelPixelType label) const
- {
- StatisticsMapConstIterator mapIt;
-
- mapIt = m_LabelStatistics.find(label);
- if ( mapIt == m_LabelStatistics.end() )
- {
- mitkThrow() << "Label does not exist";
- }
- else
- {
- return ( *mapIt ).second.m_UPP;
- }
- }
-
- template< class TInputImage, class TLabelImage >
- typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::GetMPP(LabelPixelType label) const
- {
- StatisticsMapConstIterator mapIt;
-
- mapIt = m_LabelStatistics.find(label);
- if ( mapIt == m_LabelStatistics.end() )
- {
- mitkThrow() << "Label does not exist";
- }
- else
- {
- return ( *mapIt ).second.m_MPP;
- }
- }
-
- template< class TInputImage, class TLabelImage >
- typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::GetKurtosis(LabelPixelType label) const
- {
- StatisticsMapConstIterator mapIt;
-
- mapIt = m_LabelStatistics.find(label);
- if ( mapIt == m_LabelStatistics.end() )
- {
- mitkThrow() << "Label does not exist";
- }
- else
- {
- return ( *mapIt ).second.m_Kurtosis;
- }
- }
-
-
- template< class TInputImage, class TLabelImage >
- typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::GetSkewness(LabelPixelType label) const
- {
- StatisticsMapConstIterator mapIt;
-
- mapIt = m_LabelStatistics.find(label);
- if ( mapIt == m_LabelStatistics.end() )
- {
- mitkThrow() << "Label does not exist";
- }
- else
- {
- return ( *mapIt ).second.m_Skewness;
- }
- }
-
-
-
- template< typename TInputImage, typename TLabelImage >
- typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::GetMinimum(LabelPixelType label) const
- {
- StatisticsMapConstIterator mapIt;
-
- mapIt = m_LabelStatistics.find(label);
- if ( mapIt == m_LabelStatistics.end() )
- {
- mitkThrow() << "Label does not exist";
- }
- else
- {
- return ( *mapIt ).second.m_Minimum;
- }
- }
-
- template< typename TInputImage, typename TLabelImage >
- typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::GetMaximum(LabelPixelType label) const
- {
- StatisticsMapConstIterator mapIt;
-
- mapIt = m_LabelStatistics.find(label);
- if ( mapIt == m_LabelStatistics.end() )
- {
- mitkThrow() << "Label does not exist";
- }
- else
- {
- return ( *mapIt ).second.m_Maximum;
- }
- }
-
- template< typename TInputImage, typename TLabelImage >
- typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::GetMean(LabelPixelType label) const
- {
- StatisticsMapConstIterator mapIt;
-
- mapIt = m_LabelStatistics.find(label);
- if ( mapIt == m_LabelStatistics.end() )
- {
- mitkThrow() << "Label does not exist";
- }
- else
- {
- return ( *mapIt ).second.m_Mean;
- }
- }
-
- template< typename TInputImage, typename TLabelImage >
- typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::GetSum(LabelPixelType label) const
- {
- StatisticsMapConstIterator mapIt;
-
- mapIt = m_LabelStatistics.find(label);
- if ( mapIt == m_LabelStatistics.end() )
- {
- mitkThrow() << "Label does not exist";
- }
- else
- {
- return ( *mapIt ).second.m_Sum;
- }
- }
-
- template< typename TInputImage, typename TLabelImage >
- typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::GetSigma(LabelPixelType label) const
- {
- StatisticsMapConstIterator mapIt;
-
- mapIt = m_LabelStatistics.find(label);
- if ( mapIt == m_LabelStatistics.end() )
- {
- mitkThrow() << "Label does not exist";
- }
- else
- {
- return ( *mapIt ).second.m_Sigma;
- }
- }
-
- template< typename TInputImage, typename TLabelImage >
- typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::GetVariance(LabelPixelType label) const
- {
- StatisticsMapConstIterator mapIt;
-
- mapIt = m_LabelStatistics.find(label);
- if ( mapIt == m_LabelStatistics.end() )
- {
- mitkThrow() << "Label does not exist";
- }
- else
- {
- return ( *mapIt ).second.m_Variance;
- }
- }
-
- template< typename TInputImage, typename TLabelImage >
- typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::BoundingBoxType
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::GetBoundingBox(LabelPixelType label) const
- {
- StatisticsMapConstIterator mapIt;
-
- mapIt = m_LabelStatistics.find(label);
- if ( mapIt == m_LabelStatistics.end() )
- {
- mitkThrow() << "Label does not exist";
- }
- else
- {
- return ( *mapIt ).second.m_BoundingBox;
- }
- }
-
- template< typename TInputImage, typename TLabelImage >
- typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RegionType
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::GetRegion(LabelPixelType label) const
- {
- StatisticsMapConstIterator mapIt;
-
- mapIt = m_LabelStatistics.find(label);
-
- if ( mapIt == m_LabelStatistics.end() )
- {
- mitkThrow() << "Label does not exist";
- }
- else
- {
- typename Superclass::BoundingBoxType bbox = this->GetBoundingBox(label);
- typename Superclass::IndexType index;
- typename Superclass::SizeType size;
-
- unsigned int dimension = bbox.size() / 2;
-
- for ( unsigned int i = 0; i < dimension; i++ )
- {
- index[i] = bbox[2 * i];
- size[i] = bbox[2 * i + 1] - bbox[2 * i] + 1;
- }
- typename Superclass::RegionType region;
- region.SetSize(size);
- region.SetIndex(index);
-
- return region;
- }
- }
-
- template< typename TInputImage, typename TLabelImage >
- typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::MapSizeType
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::GetCount(LabelPixelType label) const
- {
- StatisticsMapConstIterator mapIt;
-
- mapIt = m_LabelStatistics.find(label);
- if ( mapIt == m_LabelStatistics.end() )
- {
- mitkThrow() << "Label does not exist";
- }
- else
- {
- return ( *mapIt ).second.m_Count;
- }
- }
-
-
- template< typename TInputImage, typename TLabelImage >
- typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::HistogramType::Pointer
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::GetHistogram(LabelPixelType label) const
- {
- StatisticsMapConstIterator mapIt;
-
- mapIt = m_LabelStatistics.find(label);
- if ( mapIt == m_LabelStatistics.end() )
- {
- mitkThrow() << "Label does not exist";
- }
- else
- {
- // this will be zero if histograms have not been enabled
- return ( *mapIt ).second.m_Histogram;
- }
- }
-
-
-
- template< typename TInputImage, typename TLabelImage >
- void
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::BeforeThreadedGenerateData()
- {
- ThreadIdType numberOfThreads = this->GetNumberOfThreads();
-
- // Resize the thread temporaries
- m_LabelStatisticsPerThread.resize(numberOfThreads);
-
- // Initialize the temporaries
- for ( ThreadIdType i = 0; i < numberOfThreads; ++i )
- {
- m_LabelStatisticsPerThread[i].clear();
- }
-
- // Initialize the final map
- m_LabelStatistics.clear();
- }
-
- template< typename TInputImage, typename TLabelImage >
- std::list<int>
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::GetRelevantLabels() const
- {
- std::list< int> relevantLabels;
- for (int i = 0; i < 4096; ++i )
- {
- if ( this->HasLabel( i ) )
- {
- relevantLabels.push_back( i );
- }
- }
- return relevantLabels;
- }
-
- template< typename TInputImage, typename TLabelImage >
- void
- ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >
- ::ThreadedGenerateData(const typename TInputImage::RegionType & outputRegionForThread,
- ThreadIdType threadId)
- {
-
- typename HistogramType::IndexType histogramIndex(1);
- typename HistogramType::MeasurementVectorType histogramMeasurement(1);
-
- const SizeValueType size0 = outputRegionForThread.GetSize(0);
- if( size0 == 0)
- {
- return;
- }
-
- ImageLinearConstIteratorWithIndex< TInputImage > it (this->GetInput(),
- outputRegionForThread);
-
- ImageScanlineConstIterator< TLabelImage > labelIt (this->GetLabelInput(),
- outputRegionForThread);
-
- StatisticsMapIterator mapIt;
-
- // support progress methods/callbacks
- const size_t numberOfLinesToProcess = outputRegionForThread.GetNumberOfPixels() / size0;
- ProgressReporter progress( this, threadId, numberOfLinesToProcess );
-
- typedef typename MapType::value_type MapValueType;
-
- // do the work
- while ( !it.IsAtEnd() )
- {
- while ( !it.IsAtEndOfLine() )
- {
- const RealType & value = static_cast< RealType >( it.Get() );
-
- const LabelPixelType & label = labelIt.Get();
-
- // is the label already in this thread?
- mapIt = m_LabelStatisticsPerThread[threadId].find(label);
- if ( mapIt == m_LabelStatisticsPerThread[threadId].end() )
- {
- // if global histogram parameters are set and preferred then use them
- if ( m_PreferGlobalHistogramParameters && m_GlobalHistogramParametersSet )
- {
- mapIt = m_LabelStatisticsPerThread[threadId].insert( MapValueType( label,
- LabelStatistics(m_NumBins[0], m_LowerBound,
- m_UpperBound) ) ).first;
- }
- // if we have label histogram parameters then use them. If we encounter a label that has no parameters then use global settings if available
- else if(!m_PreferGlobalHistogramParameters && m_LabelHistogramParametersSet)
- {
- typename std::map<LabelPixelType, PixelType>::iterator lbIt, ubIt;
- typename std::map<LabelPixelType, unsigned int>::iterator nbIt;
-
- lbIt = m_LabelMin.find(label);
- ubIt = m_LabelMax.find(label);
- nbIt = m_LabelNBins.find(label);
-
- // if any of the parameters is lacking for the current label but global histogram params are available, use the global parameters
- if ((lbIt == m_LabelMin.end() || ubIt == m_LabelMax.end() || nbIt == m_LabelNBins.end()) && m_GlobalHistogramParametersSet)
- {
- mapIt = m_LabelStatisticsPerThread[threadId].insert( MapValueType( label,
- LabelStatistics(m_NumBins[0], m_LowerBound,
- m_UpperBound) ) ).first;
- }
- // if any of the parameters is lacking for the current label and global histogram params are not available, dont use histograms for this label
- else if ((lbIt == m_LabelMin.end() || ubIt == m_LabelMax.end() || nbIt == m_LabelNBins.end()) && !m_GlobalHistogramParametersSet)
- {
- mapIt = m_LabelStatisticsPerThread[threadId].insert( MapValueType( label,
- LabelStatistics() ) ).first;
- }
- // label histogram parameters are available, use them!
- else
- {
- PixelType lowerBound, upperBound;
- unsigned int nBins;
- lowerBound = (*lbIt).second;
- upperBound = (*ubIt).second;
- nBins = (*nbIt).second;
- mapIt = m_LabelStatisticsPerThread[threadId].insert( MapValueType( label,
- LabelStatistics(nBins, lowerBound, upperBound) ) ).first;
- }
- }
- // neither global nor label specific histogram parameters are set -> don't use histograms
- else
- {
- mapIt = m_LabelStatisticsPerThread[threadId].insert( MapValueType( label,
- LabelStatistics() ) ).first;
- }
- }
-
- typename MapType::mapped_type &labelStats = ( *mapIt ).second;
-
- // update the values for this label and this thread
- if ( value < labelStats.m_Minimum )
- {
- labelStats.m_Minimum = value;
- }
- if ( value > labelStats.m_Maximum )
- {
- labelStats.m_Maximum = value;
- }
-
- // bounding box is min,max pairs
- for ( unsigned int i = 0; i < ( 2 * TInputImage::ImageDimension ); i += 2 )
- {
- const typename TInputImage::IndexType & index = it.GetIndex();
- if ( labelStats.m_BoundingBox[i] > index[i / 2] )
- {
- labelStats.m_BoundingBox[i] = index[i / 2];
- }
- if ( labelStats.m_BoundingBox[i + 1] < index[i / 2] )
- {
- labelStats.m_BoundingBox[i + 1] = index[i / 2];
- }
- }
-
- labelStats.m_Sum += value;
- labelStats.m_SumOfSquares += ( value * value );
- labelStats.m_Count++;
- labelStats.m_SumOfCubes += std::pow(value, 3.);
- labelStats.m_SumOfQuadruples += std::pow(value, 4.);
-
- if (value > 0)
- {
- labelStats.m_PositivePixelCount++;
- labelStats.m_SumOfPositivePixels += value;
- }
-
- // if enabled, update the histogram for this label
- if ( labelStats.m_Histogram.IsNotNull() )
- {
- histogramMeasurement[0] = value;
- labelStats.m_Histogram->GetIndex(histogramMeasurement, histogramIndex);
- labelStats.m_Histogram->IncreaseFrequencyOfIndex(histogramIndex, 1);
- }
-
- ++labelIt;
- ++it;
- }
- labelIt.NextLine();
- it.NextLine();
- progress.CompletedPixel();
- }
-
- }
-
-
- template< class TInputImage, class TLabelImage >
- void ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::
- AfterThreadedGenerateData()
- {
- StatisticsMapIterator mapIt;
- StatisticsMapConstIterator threadIt;
- ThreadIdType i;
- ThreadIdType numberOfThreads = this->GetNumberOfThreads();
-
- // Run through the map for each thread and accumulate the count,
- // sum, and sumofsquares
- for ( i = 0; i < numberOfThreads; i++ )
- {
- // iterate over the map for this thread
- for ( threadIt = m_LabelStatisticsPerThread[i].begin();
- threadIt != m_LabelStatisticsPerThread[i].end();
- ++threadIt )
- {
- // does this label exist in the cumulative structure yet?
- mapIt = m_LabelStatistics.find( ( *threadIt ).first );
- if ( mapIt == m_LabelStatistics.end() )
- {
- // create a new entry
- typedef typename MapType::value_type MapValueType;
- if ( m_GlobalHistogramParametersSet || m_LabelHistogramParametersSet )
- {
-// mapIt = m_LabelStatistics.insert( MapValueType( ( *threadIt ).first,
-// LabelStatistics(m_NumBins[0], m_LowerBound,
-// m_UpperBound) ) ).first;
- mapIt = m_LabelStatistics.insert( MapValueType( *threadIt ) ).first;
- continue;
- }
- else
- {
- mapIt = m_LabelStatistics.insert( MapValueType( ( *threadIt ).first,
- LabelStatistics() ) ).first;
- }
- }
-
-
- typename MapType::mapped_type &labelStats = ( *mapIt ).second;
-
- // accumulate the information from this thread
- labelStats.m_Count += ( *threadIt ).second.m_Count;
- labelStats.m_Sum += ( *threadIt ).second.m_Sum;
- labelStats.m_SumOfSquares += ( *threadIt ).second.m_SumOfSquares;
- labelStats.m_SumOfPositivePixels += ( *threadIt ).second.m_SumOfPositivePixels;
- labelStats.m_PositivePixelCount += ( *threadIt ).second.m_PositivePixelCount;
- labelStats.m_SumOfCubes += ( *threadIt ).second.m_SumOfCubes;
- labelStats.m_SumOfQuadruples += ( *threadIt ).second.m_SumOfQuadruples;
-
- if ( labelStats.m_Minimum > ( *threadIt ).second.m_Minimum )
- {
- labelStats.m_Minimum = ( *threadIt ).second.m_Minimum;
- }
- if ( labelStats.m_Maximum < ( *threadIt ).second.m_Maximum )
- {
- labelStats.m_Maximum = ( *threadIt ).second.m_Maximum;
- }
-
- //bounding box is min,max pairs
- int dimension = labelStats.m_BoundingBox.size() / 2;
- for ( int ii = 0; ii < ( dimension * 2 ); ii += 2 )
- {
- if ( labelStats.m_BoundingBox[ii] > ( *threadIt ).second.m_BoundingBox[ii] )
- {
- labelStats.m_BoundingBox[ii] = ( *threadIt ).second.m_BoundingBox[ii];
- }
- if ( labelStats.m_BoundingBox[ii + 1] < ( *threadIt ).second.m_BoundingBox[ii + 1] )
- {
- labelStats.m_BoundingBox[ii + 1] = ( *threadIt ).second.m_BoundingBox[ii + 1];
- }
- }
-
- // if enabled, update the histogram for this label
- if ( m_GlobalHistogramParametersSet || m_LabelHistogramParametersSet )
- {
- typename HistogramType::IndexType index;
- index.SetSize(1);
- for ( unsigned int bin = 0; bin < labelStats.m_Histogram->Size(); bin++ )
- {
- index[0] = bin;
- labelStats.m_Histogram->IncreaseFrequency( bin, ( *threadIt ).second.m_Histogram->GetFrequency(bin) );
- }
- }
- } // end of thread map iterator loop
- } // end of thread loop
-
- // compute the remainder of the statistics
- for ( mapIt = m_LabelStatistics.begin();
- mapIt != m_LabelStatistics.end();
- ++mapIt )
- {
- typename MapType::mapped_type &labelStats = ( *mapIt ).second;
-
- // mean
- labelStats.m_Mean = labelStats.m_Sum
- / static_cast< RealType >( labelStats.m_Count );
-
- // MPP
- labelStats.m_MPP = labelStats.m_SumOfPositivePixels
- / static_cast< RealType >( labelStats.m_PositivePixelCount );
-
- // variance
- if ( labelStats.m_Count > 0 )
- {
- // unbiased estimate of variance
- LabelStatistics & ls = mapIt->second;
- const RealType sumSquared = ls.m_Sum * ls.m_Sum;
- const RealType count = static_cast< RealType >( ls.m_Count );
-
- ls.m_Variance = ( ls.m_SumOfSquares - sumSquared / count ) / ( count );
-
- RealType secondMoment = ls.m_SumOfSquares / count;
- RealType thirdMoment = ls.m_SumOfCubes / count;
- RealType fourthMoment = ls.m_SumOfQuadruples / count;
-
- ls.m_Skewness = (thirdMoment - 3. * secondMoment * ls.m_Mean + 2. * std::pow(ls.m_Mean, 3.)) / std::pow(secondMoment - std::pow(ls.m_Mean, 2.), 1.5); // see http://www.boost.org/doc/libs/1_51_0/doc/html/boost/accumulators/impl/skewness_impl.html
- ls.m_Kurtosis = (fourthMoment - 4. * thirdMoment * ls.m_Mean + 6. * secondMoment * std::pow(ls.m_Mean, 2.) - 3. * std::pow(ls.m_Mean, 4.)) / std::pow(secondMoment - std::pow(ls.m_Mean, 2.), 2.); // see http://www.boost.org/doc/libs/1_51_0/doc/html/boost/accumulators/impl/kurtosis_impl.html, dropped -3
- }
- else
- {
- labelStats.m_Variance = NumericTraits< RealType >::ZeroValue();
- labelStats.m_Skewness = NumericTraits< RealType >::ZeroValue();
- labelStats.m_Kurtosis = NumericTraits< RealType >::ZeroValue();
- }
-
- // sigma
- labelStats.m_Sigma = std::sqrt( labelStats.m_Variance );
-
- // histogram statistics
- if (labelStats.m_Histogram.IsNotNull())
- {
- mitk::HistogramStatisticsCalculator histStatCalc;
- histStatCalc.SetHistogram(labelStats.m_Histogram);
- histStatCalc.CalculateStatistics();
- labelStats.m_Median = histStatCalc.GetMedian();
- labelStats.m_Entropy = histStatCalc.GetEntropy();
- labelStats.m_Uniformity = histStatCalc.GetUniformity();
- labelStats.m_UPP = histStatCalc.GetUPP();
- }
-
- }
-
- {
- //Now update the cached vector of valid labels.
- m_ValidLabelValues.resize(0);
- m_ValidLabelValues.reserve(m_LabelStatistics.size());
- for ( mapIt = m_LabelStatistics.begin();
- mapIt != m_LabelStatistics.end();
- ++mapIt )
- {
- m_ValidLabelValues.push_back(mapIt->first);
- }
- }
- }
-
-} // end namespace itk
-
-#endif
diff --git a/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.h b/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.h
deleted file mode 100644
index 00236bce16..0000000000
--- a/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.h
+++ /dev/null
@@ -1,207 +0,0 @@
-/*============================================================================
-
-The Medical Imaging Interaction Toolkit (MITK)
-
-Copyright (c) German Cancer Research Center (DKFZ)
-All rights reserved.
-
-Use of this source code is governed by a 3-clause BSD license that can be
-found in the LICENSE file.
-
-============================================================================*/
-#ifndef __mitkExtendedStatisticsImageFilter
-#define __mitkExtendedStatisticsImageFilter
-
-#include "itkStatisticsImageFilter.h"
-#include <mbilog.h>
-#include <mitkLogMacros.h>
-
-namespace itk
-{
- /**
- * \class ExtendedStatisticsImageFilter
- * \brief Extension of the itkStatisticsImageFilter that also calculates the Skewness and Kurtosis.
- *
- * This class inherits from the itkStatisticsImageFilter and
- * uses its results for the calculation of other additional coefficients:
- * the Skewness and Kurtosis.
- *
- * As the StatisticsImageFilter stores the statistics in the outputs 1 to 6 by the
- * StatisticsImageFilter, the skewness, kurtosis, MPP, UPP, Uniformity, Entropy and Median are stored in the outputs
- * 7 to 14 by this filter.
- */
- template< class TInputImage >
- class ExtendedStatisticsImageFilter : public StatisticsImageFilter< TInputImage >
- {
- public:
- /** Standard Self typedef */
- typedef ExtendedStatisticsImageFilter Self;
- typedef StatisticsImageFilter< TInputImage > Superclass;
- typedef SmartPointer< Self > Pointer;
- typedef SmartPointer< const Self > ConstPointer;
- typedef typename Superclass::RealType RealType;
- typedef typename Superclass::RealObjectType RealObjectType;
- typedef typename Superclass::PixelType PixelType;
-
- /** Histogram-related typedefs */
- typedef itk::Statistics::Histogram< RealType > HistogramType;
- typedef typename HistogramType::Pointer HistogramPointer;
-
- itkFactorylessNewMacro( Self );
- itkCloneMacro( Self );
- itkTypeMacro( ExtendedStatisticsImageFilter, StatisticsImageFilter );
-
- /**
- * \brief Return the computed Skewness.
- */
- double GetSkewness() const
- {
- return this->GetSkewnessOutput()->Get();
- }
-
- /**
- * \brief Return the computed Median
- */
- double GetMedian() const
- {
- return this->GetMedianOutput()->Get();
- }
-
- /**
- * \brief Return the computed Kurtosis.
- */
- double GetKurtosis() const
- {
- return this->GetKurtosisOutput()->Get();
- }
-
- /* \brief Return the computed MPP.
- */
- double GetMPP() const
- {
- return this->GetMPPOutput()->Get();
- }
-
- /**
- * \brief Return the computed Uniformity.
- */
- double GetUniformity() const
- {
- return this->GetUniformityOutput()->Get();
- }
-
- /**
- *\brief Return the computed Entropy.
- */
- double GetEntropy() const
- {
- return this->GetEntropyOutput()->Get();
- }
-
- /**
- * \brief Return the computed UPP.
- */
- double GetUPP() const
- {
- return this->GetUPPOutput()->Get();
- }
-
-
- /**
- * \brief Return the computed Histogram.
- */
- const typename HistogramType::Pointer
- GetHistogram()
- {
- if (m_HistogramCalculated)
- {
- return m_Histogram;
- }
- else
- {
- return nullptr;
- }
- }
-
-
- /** specify Histogram parameters */
- void SetHistogramParameters(const int numBins, RealType lowerBound,
- RealType upperBound);
-
- protected:
-
- ExtendedStatisticsImageFilter();
-
- ~ExtendedStatisticsImageFilter() override{};
-
- void BeforeThreadedGenerateData() override;
-
- /** Multi-thread version GenerateData. */
- void ThreadedGenerateData(const typename StatisticsImageFilter<TInputImage>::RegionType &
- outputRegionForThread,
- ThreadIdType threadId) override;
-
- /**
- * brief Calls AfterThreadedGenerateData() of the superclass and the main methods
- */
- void AfterThreadedGenerateData() override;
-
-
- RealObjectType* GetSkewnessOutput();
-
- const RealObjectType* GetSkewnessOutput() const;
-
- RealObjectType* GetKurtosisOutput();
-
- const RealObjectType* GetKurtosisOutput() const;
-
- RealObjectType* GetMPPOutput();
-
- const RealObjectType* GetMPPOutput() const;
-
- RealObjectType* GetEntropyOutput();
-
- const RealObjectType* GetEntropyOutput() const;
-
- RealObjectType* GetUniformityOutput();
-
- const RealObjectType* GetUniformityOutput() const;
-
- RealObjectType* GetUPPOutput();
-
- const RealObjectType* GetUPPOutput() const;
-
- RealObjectType* GetMedianOutput();
-
- const RealObjectType* GetMedianOutput() const;
-
- using Superclass::MakeOutput;
- DataObject::Pointer MakeOutput( ProcessObject::DataObjectPointerArraySizeType idx ) override;
-
-private:
- Array< RealType > m_ThreadSum;
- Array< RealType > m_SumOfSquares;
- Array< RealType > m_SumOfCubes;
- Array< RealType > m_SumOfQuadruples;
- Array< SizeValueType > m_Count;
- Array< SizeValueType > m_PositivePixelCount;
- Array< RealType > m_ThreadSumOfPositivePixels;
- Array< PixelType > m_ThreadMin;
- Array< PixelType > m_ThreadMax;
- std::vector< HistogramPointer > m_HistogramPerThread;
- HistogramPointer m_Histogram;
- bool m_UseHistogram;
- bool m_HistogramCalculated;
- RealType m_LowerBound, m_UpperBound;
- int m_NumBins;
-
-
- }; // end of class
-
-} // end namespace itk
-
-#ifndef ITK_MANUAL_INSTANTIATION
-#include "mitkExtendedStatisticsImageFilter.hxx"
-#endif
-
-#endif
diff --git a/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.hxx b/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.hxx
deleted file mode 100644
index 7ade1af4e9..0000000000
--- a/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.hxx
+++ /dev/null
@@ -1,470 +0,0 @@
-/*============================================================================
-
-The Medical Imaging Interaction Toolkit (MITK)
-
-Copyright (c) German Cancer Research Center (DKFZ)
-All rights reserved.
-
-Use of this source code is governed by a 3-clause BSD license that can be
-found in the LICENSE file.
-
-============================================================================*/
-#ifndef __mitkExtendedStatisticsImageFilter_hxx
-#define __mitkExtendedStatisticsImageFilter_hxx
-
-#include "mitkExtendedStatisticsImageFilter.h"
-#include <mitkHistogramStatisticsCalculator.h>
-
-
-namespace itk
-{
- template< class TInputImage >
- ExtendedStatisticsImageFilter< TInputImage >::ExtendedStatisticsImageFilter()
- : StatisticsImageFilter< TInputImage >(),
- m_ThreadSum(1),
- m_SumOfSquares(1),
- m_SumOfCubes(1),
- m_SumOfQuadruples(1),
- m_Count(1),
- m_PositivePixelCount(1),
- m_ThreadSumOfPositivePixels(1),
- m_ThreadMin(1),
- m_ThreadMax(1),
- m_UseHistogram(false),
- m_HistogramCalculated(false)
- {
- /*
- * add the Skewness,Kurtosis,Entropy,Uniformity,MPP, UPP, Median to the other statistical calculated Values
- * of the mitkStatisticsImageFilter as the 7th to the 13th Output
- */
- for ( int i = 7; i < 14; ++i )
- {
- typename RealObjectType::Pointer output =
- static_cast< RealObjectType * >( this->MakeOutput(i).GetPointer() );
- this->ProcessObject::SetNthOutput( i, output.GetPointer() );
- }
-
- this->GetSkewnessOutput()->Set( 0.0 );
- this->GetKurtosisOutput()->Set( 0.0 );
- this->GetEntropyOutput()->Set( -1.0 );
- this->GetUniformityOutput()->Set( 0.0 );
- this->GetUPPOutput()->Set( 0.0 );
- this->GetMPPOutput()->Set( 0.0 );
- this->GetMedianOutput()->Set( 0.0 );
-
- m_Histogram = ITK_NULLPTR;
- m_HistogramPerThread.resize(0);
- }
-
- template< class TInputImage >
- DataObject::Pointer
- ExtendedStatisticsImageFilter< TInputImage >::MakeOutput( ProcessObject::DataObjectPointerArraySizeType output)
- {
- switch ( output )
- {
- case 7:
- case 8:
- case 9:
- case 10:
- case 11:
- case 12:
- {
- return RealObjectType::New().GetPointer();
- }
- case 13:
- {
- return RealObjectType::New().GetPointer();
- }
- default:
- {
- // might as well make an image
- return Superclass::MakeOutput( output );
- }
- }
- }
-
- template< class TInputImage >
- typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType *
- ExtendedStatisticsImageFilter< TInputImage >
- ::GetSkewnessOutput()
- {
- return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(7) );
- }
-
- template< class TInputImage >
- const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType *
- ExtendedStatisticsImageFilter< TInputImage >
- ::GetSkewnessOutput() const
- {
- return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(7) );
- }
-
-
- template< class TInputImage >
- typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType *
- ExtendedStatisticsImageFilter< TInputImage >
- ::GetKurtosisOutput()
- {
- return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(8) );
- }
-
- template< class TInputImage >
- const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType *
- ExtendedStatisticsImageFilter< TInputImage >
- ::GetKurtosisOutput() const
- {
- return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(8) );
- }
-
- template< class TInputImage >
- typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType *
- ExtendedStatisticsImageFilter< TInputImage >
- ::GetUniformityOutput()
- {
- return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(9) );
- }
-
- template< class TInputImage >
- const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType *
- ExtendedStatisticsImageFilter< TInputImage >
- ::GetUniformityOutput() const
- {
- return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(9) );
- }
-
-
- template< class TInputImage >
- typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType *
- ExtendedStatisticsImageFilter< TInputImage >
- ::GetEntropyOutput()
- {
- return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(10) );
- }
-
- template< class TInputImage >
- const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType *
- ExtendedStatisticsImageFilter< TInputImage >
- ::GetEntropyOutput() const
- {
- return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(10) );
- }
-
- template< class TInputImage >
- typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType *
- ExtendedStatisticsImageFilter< TInputImage >
- ::GetUPPOutput()
- {
- return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(11) );
- }
-
- template< class TInputImage >
- const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType *
- ExtendedStatisticsImageFilter< TInputImage >
- ::GetUPPOutput() const
- {
- return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(11) );
- }
-
- template< class TInputImage >
- typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType *
- ExtendedStatisticsImageFilter< TInputImage >
- ::GetMPPOutput()
- {
- return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(12) );
- }
-
- template< class TInputImage >
- const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType *
- ExtendedStatisticsImageFilter< TInputImage >
- ::GetMPPOutput() const
- {
- return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(12) );
- }
-
- template< class TInputImage >
- typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType *
- ExtendedStatisticsImageFilter< TInputImage >
- ::GetMedianOutput()
- {
- return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(13) );
- }
-
- template< class TInputImage >
- const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType *
- ExtendedStatisticsImageFilter< TInputImage >
- ::GetMedianOutput() const
- {
- return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(13) );
- }
-
-
- template< typename TInputImage >
- void
- ExtendedStatisticsImageFilter< TInputImage >
- ::BeforeThreadedGenerateData()
- {
- ThreadIdType numberOfThreads = this->GetNumberOfThreads();
-
- if (m_UseHistogram)
- {
- // Histogram
- m_Histogram = HistogramType::New();
- typename HistogramType::SizeType hsize;
- typename HistogramType::MeasurementVectorType lb;
- typename HistogramType::MeasurementVectorType ub;
- hsize.SetSize(1);
- lb.SetSize(1);
- ub.SetSize(1);
- m_Histogram->SetMeasurementVectorSize(1);
- hsize[0] = m_NumBins;
- lb[0] = m_LowerBound;
- ub[0] = m_UpperBound;
- m_Histogram->Initialize(hsize, lb, ub);
-
- m_HistogramPerThread.resize(numberOfThreads);
-
- for (unsigned int i = 0; i < numberOfThreads; i++)
- {
- typename HistogramType::Pointer hist = HistogramType::New();
- typename HistogramType::SizeType hsize;
- typename HistogramType::MeasurementVectorType lb;
- typename HistogramType::MeasurementVectorType ub;
- hsize.SetSize(1);
- lb.SetSize(1);
- ub.SetSize(1);
- hist->SetMeasurementVectorSize(1);
- hsize[0] = m_NumBins;
- lb[0] = m_LowerBound;
- ub[0] = m_UpperBound;
- hist->Initialize(hsize, lb, ub);
- m_HistogramPerThread[i] = hist;
- }
- }
-
- // Resize the thread temporaries
- m_Count.SetSize(numberOfThreads);
- m_SumOfSquares.SetSize(numberOfThreads);
- m_SumOfCubes.SetSize(numberOfThreads);
- m_SumOfQuadruples.SetSize(numberOfThreads);
- m_ThreadSum.SetSize(numberOfThreads);
- m_ThreadMin.SetSize(numberOfThreads);
- m_ThreadMax.SetSize(numberOfThreads);
- m_PositivePixelCount.SetSize(numberOfThreads);
- m_ThreadSumOfPositivePixels.SetSize(numberOfThreads);
-
- // Initialize the temporaries
- m_Count.Fill(NumericTraits< SizeValueType >::Zero);
- m_ThreadSum.Fill(NumericTraits< RealType >::Zero);
- m_SumOfSquares.Fill(NumericTraits< RealType >::Zero);
- m_SumOfCubes.Fill(NumericTraits< RealType >::Zero);
- m_SumOfQuadruples.Fill(NumericTraits< RealType >::Zero);
- m_ThreadMin.Fill( NumericTraits< PixelType >::max() );
- m_ThreadMax.Fill( NumericTraits< PixelType >::NonpositiveMin() );
- m_PositivePixelCount.Fill(NumericTraits< SizeValueType >::Zero);
- m_ThreadSumOfPositivePixels.Fill(NumericTraits< SizeValueType >::Zero);
- }
-
-
- template< typename TInputImage >
- void
- ExtendedStatisticsImageFilter< TInputImage >
- ::ThreadedGenerateData(const typename StatisticsImageFilter<TInputImage>::RegionType &
- outputRegionForThread,
- ThreadIdType threadId)
- {
- const SizeValueType size0 = outputRegionForThread.GetSize(0);
- if( size0 == 0)
- {
- return;
- }
- RealType realValue;
- PixelType value;
-
- typename HistogramType::IndexType histogramIndex(1);
- typename HistogramType::MeasurementVectorType histogramMeasurement(1);
-
- RealType sum = NumericTraits< RealType >::ZeroValue();
- RealType sumOfPositivePixels = NumericTraits< RealType >::ZeroValue();
- RealType sumOfSquares = NumericTraits< RealType >::ZeroValue();
- RealType sumOfCubes = NumericTraits< RealType >::ZeroValue();
- RealType sumOfQuadruples = NumericTraits< RealType >::ZeroValue();
- SizeValueType count = NumericTraits< SizeValueType >::ZeroValue();
- SizeValueType countOfPositivePixels = NumericTraits< SizeValueType >::ZeroValue();
- PixelType min = NumericTraits< PixelType >::max();
- PixelType max = NumericTraits< PixelType >::NonpositiveMin();
-
- ImageScanlineConstIterator< TInputImage > it (this->GetInput(), outputRegionForThread);
-
- // support progress methods/callbacks
- const size_t numberOfLinesToProcess = outputRegionForThread.GetNumberOfPixels() / size0;
- ProgressReporter progress( this, threadId, numberOfLinesToProcess );
-
- // do the work
- while ( !it.IsAtEnd() )
- {
- while ( !it.IsAtEndOfLine() )
- {
- value = it.Get();
- realValue = static_cast< RealType >( value );
-
- if (m_UseHistogram)
- {
- histogramMeasurement[0] = value;
- m_HistogramPerThread[threadId]->GetIndex(histogramMeasurement, histogramIndex);
- m_HistogramPerThread[threadId]->IncreaseFrequencyOfIndex(histogramIndex, 1);
- }
-
- if ( value < min )
- {
- min = value;
- }
- if ( value > max )
- {
- max = value;
- }
- if (value > 0)
- {
- sumOfPositivePixels += realValue;
- ++countOfPositivePixels;
- }
-
- sum += realValue;
- sumOfSquares += ( realValue * realValue );
- sumOfCubes += std::pow(realValue, 3.);
- sumOfQuadruples += std::pow(realValue, 4.);
- ++count;
- ++it;
- }
- it.NextLine();
- progress.CompletedPixel();
- }
-
- m_ThreadSum[threadId] = sum;
- m_SumOfSquares[threadId] = sumOfSquares;
- m_Count[threadId] = count;
- m_ThreadMin[threadId] = min;
- m_ThreadMax[threadId] = max;
- m_ThreadSumOfPositivePixels[threadId] = sumOfPositivePixels;
- m_PositivePixelCount[threadId] = countOfPositivePixels;
- m_SumOfCubes[threadId] = sumOfCubes;
- m_SumOfQuadruples[threadId] = sumOfQuadruples;
- }
-
-
- template< class TInputImage >
- void
- ExtendedStatisticsImageFilter< TInputImage >
- ::AfterThreadedGenerateData()
- {
- ThreadIdType i;
- SizeValueType count = 0;
- SizeValueType countOfPositivePixels = 0;
- RealType sumOfSquares = 0;
- RealType sumOfCubes = 0;
- RealType sumOfQuadruples = 0;
- RealType sumOfPositivePixels = 0;
-
- ThreadIdType numberOfThreads = this->GetNumberOfThreads();
-
- PixelType minimum;
- PixelType maximum;
- RealType mean;
- RealType meanOfPositivePixels;
- RealType sigma;
- RealType variance;
- RealType skewness;
- RealType kurtosis;
- RealType sum;
-
- sum = sumOfSquares = sumOfCubes = sumOfQuadruples = NumericTraits< RealType >::ZeroValue();
- count = countOfPositivePixels = 0;
-
- // Find the min/max over all threads and accumulate count, sum and
- // sum of squares
- minimum = NumericTraits< PixelType >::max();
- maximum = NumericTraits< PixelType >::NonpositiveMin();
- for ( i = 0; i < numberOfThreads; i++ )
- {
- count += m_Count[i];
- sum += m_ThreadSum[i];
- sumOfSquares += m_SumOfSquares[i];
- sumOfCubes += m_SumOfCubes[i];
- sumOfQuadruples += m_SumOfQuadruples[i];
- sumOfPositivePixels += m_ThreadSumOfPositivePixels[i];
- countOfPositivePixels += m_PositivePixelCount[i];
-
- if ( m_ThreadMin[i] < minimum )
- {
- minimum = m_ThreadMin[i];
- }
- if ( m_ThreadMax[i] > maximum )
- {
- maximum = m_ThreadMax[i];
- }
- // if enabled, update the histogram for this label
- if ( m_UseHistogram )
- {
- typename HistogramType::IndexType index;
- index.SetSize(1);
- for ( int bin = 0; bin < m_NumBins; ++bin )
- {
- index[0] = bin;
- m_Histogram->IncreaseFrequency( bin, m_HistogramPerThread[i]->GetFrequency(bin) );
- }
- }
- }
- // compute statistics
- mean = sum / static_cast< RealType >( count );
- meanOfPositivePixels = sumOfPositivePixels / static_cast< RealType >( countOfPositivePixels );
-
- // unbiased estimate
- variance = ( sumOfSquares - ( sum * sum / static_cast< RealType >( count ) ) )
- / ( static_cast< RealType >( count ) );
- RealType secondMoment, thirdMoment, fourthMoment;
- secondMoment = sumOfSquares / static_cast< RealType >( count );
- thirdMoment = sumOfCubes / static_cast< RealType >( count );
- fourthMoment = sumOfQuadruples / static_cast< RealType >( count );
-
- skewness = (thirdMoment - 3. * secondMoment * mean + 2. * std::pow(mean, 3.)) / std::pow(secondMoment - std::pow(mean, 2.), 1.5); // see http://www.boost.org/doc/libs/1_51_0/doc/html/boost/accumulators/impl/skewness_impl.html
- kurtosis = (fourthMoment - 4. * thirdMoment * mean + 6. * secondMoment * std::pow(mean, 2.) - 3. * std::pow(mean, 4.)) / std::pow(secondMoment - std::pow(mean, 2.), 2.); // see http://www.boost.org/doc/libs/1_46_1/doc/html/boost/accumulators/impl/kurtosis_impl.html, dropped -3
- sigma = std::sqrt(variance);
-
- // Set the outputs
- this->GetMinimumOutput()->Set(minimum);
- this->GetMaximumOutput()->Set(maximum);
- this->GetMeanOutput()->Set(mean);
- this->GetSigmaOutput()->Set(sigma);
- this->GetVarianceOutput()->Set(variance);
- this->GetSumOutput()->Set(sum);
- this->GetKurtosisOutput()->Set(kurtosis);
- this->GetSkewnessOutput()->Set(skewness);
- this->GetMPPOutput()->Set(meanOfPositivePixels);
-
- if (m_UseHistogram)
- {
- mitk::HistogramStatisticsCalculator histStatCalc;
- histStatCalc.SetHistogram(m_Histogram);
- histStatCalc.CalculateStatistics();
- this->GetUniformityOutput()->Set(histStatCalc.GetUniformity());
- this->GetUPPOutput()->Set(histStatCalc.GetUPP());
- this->GetEntropyOutput()->Set(histStatCalc.GetEntropy());
- this->GetMedianOutput()->Set(histStatCalc.GetMedian());
-
- }
- m_HistogramCalculated = true;
- }
-
- template< typename TInputImage >
- void
- ExtendedStatisticsImageFilter< TInputImage >
- ::SetHistogramParameters(const int numBins, RealType lowerBound, RealType upperBound)
- {
- m_NumBins = numBins;
- m_LowerBound = lowerBound;
- m_UpperBound = upperBound;
- m_UseHistogram = true;
- }
-
-}
-
-#endif
diff --git a/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp b/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp
index 83814b422c..30c805a19c 100644
--- a/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp
+++ b/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp
@@ -1,613 +1,612 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <mitkHotspotMaskGenerator.h>
#include <mitkImageTimeSelector.h>
#include <mitkImageCast.h>
#include <mitkPoint.h>
#include <itkImageRegionIterator.h>
#include "mitkImageAccessByItk.h"
#include <itkImageDuplicator.h>
#include <itkFFTConvolutionImageFilter.h>
#include <mitkITKImageImport.h>
namespace mitk
{
HotspotMaskGenerator::HotspotMaskGenerator():
m_HotspotRadiusinMM(6.2035049089940), // radius of a 1cm3 sphere in mm
m_HotspotMustBeCompletelyInsideImage(true),
m_Label(1)
{
m_TimeStep = 0;
m_InternalMask = mitk::Image::New();
m_InternalMaskUpdateTime = 0;
}
void HotspotMaskGenerator::SetInputImage(mitk::Image::Pointer inputImage)
{
if (inputImage != m_inputImage)
{
m_inputImage = inputImage;
m_ConvolutionImageMaxIndex.set_size(inputImage->GetDimension());
m_ConvolutionImageMinIndex.set_size(inputImage->GetDimension());
this->Modified();
}
}
void HotspotMaskGenerator::SetMask(MaskGenerator::Pointer mask)
{
if (mask != m_Mask)
{
m_Mask = mask;
this->Modified();
}
}
HotspotMaskGenerator::~HotspotMaskGenerator()
{
}
void HotspotMaskGenerator::SetHotspotRadiusInMM(double radiusInMillimeter)
{
if(radiusInMillimeter != m_HotspotRadiusinMM)
{
m_HotspotRadiusinMM = radiusInMillimeter;
this->Modified();
}
}
const double& HotspotMaskGenerator::GetHotspotRadiusinMM() const
{
return m_HotspotRadiusinMM;
}
bool HotspotMaskGenerator::GetHotspotMustBeCompletelyInsideImage() const
{
return m_HotspotMustBeCompletelyInsideImage;
}
void HotspotMaskGenerator::SetHotspotMustBeCompletelyInsideImage(bool mustBeCompletelyInImage)
{
if (m_HotspotMustBeCompletelyInsideImage != mustBeCompletelyInImage)
{
m_HotspotMustBeCompletelyInsideImage = mustBeCompletelyInImage;
this->Modified();
}
}
mitk::Image::Pointer HotspotMaskGenerator::GetMask()
{
if (IsUpdateRequired())
{
if ( m_inputImage.IsNull() )
{
throw std::runtime_error( "Error: image empty!" );
}
if ( m_TimeStep >= m_inputImage->GetTimeSteps() )
{
throw std::runtime_error( "Error: invalid time step!" );
}
mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New();
imageTimeSelector->SetInput( m_inputImage );
imageTimeSelector->SetTimeNr( m_TimeStep );
imageTimeSelector->UpdateLargestPossibleRegion();
mitk::Image::Pointer timeSliceImage = imageTimeSelector->GetOutput();
m_internalImage = timeSliceImage;
m_internalMask2D = nullptr; // is this correct when this variable holds a smart pointer?
m_internalMask3D = nullptr;
if ( m_Mask != nullptr )
{
m_Mask->SetTimeStep(m_TimeStep);
mitk::Image::Pointer timeSliceMask = m_Mask->GetMask();
if ( m_internalImage->GetDimension() == 3 )
{
CastToItkImage(timeSliceMask, m_internalMask3D);
AccessFixedDimensionByItk_2(m_internalImage, CalculateHotspotMask, 3, m_internalMask3D, m_Label);
}
else if ( m_internalImage->GetDimension() == 2 )
{
CastToItkImage(timeSliceMask, m_internalMask2D);
AccessFixedDimensionByItk_2(m_internalImage, CalculateHotspotMask, 2, m_internalMask2D, m_Label);
}
else
{
throw std::runtime_error( "Error: invalid image dimension" );
}
}
else
{
if ( m_internalImage->GetDimension() == 3 )
{
AccessFixedDimensionByItk_2(m_internalImage, CalculateHotspotMask, 3, m_internalMask3D, m_Label);
}
else if ( m_internalImage->GetDimension() == 2 )
{
AccessFixedDimensionByItk_2(m_internalImage, CalculateHotspotMask, 2, m_internalMask2D, m_Label);
}
else
{
throw std::runtime_error( "Error: invalid image dimension" );
}
}
this->Modified();
}
m_InternalMaskUpdateTime = m_InternalMask->GetMTime();
return m_InternalMask;
}
void HotspotMaskGenerator::SetTimeStep(unsigned int timeStep)
{
if (m_TimeStep != timeStep)
{
m_TimeStep = timeStep;
}
}
void HotspotMaskGenerator::SetLabel(unsigned short label)
{
if (label != m_Label)
{
m_Label = label;
this->Modified();
}
}
vnl_vector<int> HotspotMaskGenerator::GetConvolutionImageMinIndex()
{
this->GetMask(); // make sure we are up to date
return m_ConvolutionImageMinIndex;
}
vnl_vector<int> HotspotMaskGenerator::GetHotspotIndex()
{
this->GetMask(); // make sure we are up to date
return m_ConvolutionImageMaxIndex;
}
template <typename TPixel, unsigned int VImageDimension >
HotspotMaskGenerator::ImageExtrema
HotspotMaskGenerator::CalculateExtremaWorld( const itk::Image<TPixel, VImageDimension>* inputImage,
typename itk::Image<unsigned short, VImageDimension>::Pointer maskImage,
double neccessaryDistanceToImageBorderInMM,
unsigned int label )
{
typedef itk::Image< TPixel, VImageDimension > ImageType;
typedef itk::Image< unsigned short, VImageDimension > MaskImageType;
typedef itk::ImageRegionConstIteratorWithIndex<MaskImageType> MaskImageIteratorType;
typedef itk::ImageRegionConstIteratorWithIndex<ImageType> InputImageIndexIteratorType;
typename ImageType::SpacingType spacing = inputImage->GetSpacing();
ImageExtrema minMax;
minMax.Defined = false;
minMax.MaxIndex.set_size(VImageDimension);
minMax.MaxIndex.set_size(VImageDimension);
typename ImageType::RegionType allowedExtremaRegion = inputImage->GetLargestPossibleRegion();
bool keepDistanceToImageBorders( neccessaryDistanceToImageBorderInMM > 0 );
if (keepDistanceToImageBorders)
{
itk::IndexValueType distanceInPixels[VImageDimension];
for(unsigned short dimension = 0; dimension < VImageDimension; ++dimension)
{
// To confirm that the whole hotspot is inside the image we have to keep a specific distance to the image-borders, which is as long as
// the radius. To get the amount of indices we divide the radius by spacing and add 0.5 because voxels are center based:
// For example with a radius of 2.2 and a spacing of 1 two indices are enough because 2.2 / 1 + 0.5 = 2.7 => 2.
// But with a radius of 2.7 we need 3 indices because 2.7 / 1 + 0.5 = 3.2 => 3
distanceInPixels[dimension] = int( neccessaryDistanceToImageBorderInMM / spacing[dimension] + 0.5);
}
allowedExtremaRegion.ShrinkByRadius(distanceInPixels);
}
InputImageIndexIteratorType imageIndexIt(inputImage, allowedExtremaRegion);
float maxValue = itk::NumericTraits<float>::min();
float minValue = itk::NumericTraits<float>::max();
typename ImageType::IndexType maxIndex;
typename ImageType::IndexType minIndex;
for(unsigned short i = 0; i < VImageDimension; ++i)
{
maxIndex[i] = 0;
minIndex[i] = 0;
}
if (maskImage != nullptr)
{
MaskImageIteratorType maskIt(maskImage, maskImage->GetLargestPossibleRegion());
typename ImageType::IndexType imageIndex;
- typename ImageType::PointType worldPosition;
typename ImageType::IndexType maskIndex;
for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt)
{
imageIndex = maskIndex = maskIt.GetIndex();
if(maskIt.Get() == label)
{
if( allowedExtremaRegion.IsInside(imageIndex) )
{
imageIndexIt.SetIndex( imageIndex );
double value = imageIndexIt.Get();
minMax.Defined = true;
//Calculate minimum, maximum and corresponding index-values
if( value > maxValue )
{
maxIndex = imageIndexIt.GetIndex();
maxValue = value;
}
if(value < minValue )
{
minIndex = imageIndexIt.GetIndex();
minValue = value;
}
}
}
}
}
else
{
for(imageIndexIt.GoToBegin(); !imageIndexIt.IsAtEnd(); ++imageIndexIt)
{
double value = imageIndexIt.Get();
minMax.Defined = true;
//Calculate minimum, maximum and corresponding index-values
if( value > maxValue )
{
maxIndex = imageIndexIt.GetIndex();
maxValue = value;
}
if(value < minValue )
{
minIndex = imageIndexIt.GetIndex();
minValue = value;
}
}
}
minMax.MaxIndex.set_size(VImageDimension);
minMax.MinIndex.set_size(VImageDimension);
for(unsigned int i = 0; i < minMax.MaxIndex.size(); ++i)
{
minMax.MaxIndex[i] = maxIndex[i];
}
for(unsigned int i = 0; i < minMax.MinIndex.size(); ++i)
{
minMax.MinIndex[i] = minIndex[i];
}
minMax.Max = maxValue;
minMax.Min = minValue;
return minMax;
}
template <unsigned int VImageDimension>
itk::Size<VImageDimension>
HotspotMaskGenerator::CalculateConvolutionKernelSize( double spacing[VImageDimension],
double radiusInMM )
{
typedef itk::Image< float, VImageDimension > KernelImageType;
typedef typename KernelImageType::SizeType SizeType;
SizeType maskSize;
for(unsigned int i = 0; i < VImageDimension; ++i)
{
maskSize[i] = static_cast<int>( 2 * radiusInMM / spacing[i]);
// We always want an uneven size to have a clear center point in the convolution mask
if(maskSize[i] % 2 == 0 )
{
++maskSize[i];
}
}
return maskSize;
}
template <unsigned int VImageDimension>
itk::SmartPointer< itk::Image<float, VImageDimension> >
HotspotMaskGenerator::GenerateHotspotSearchConvolutionKernel(double mmPerPixel[VImageDimension],
double radiusInMM )
{
std::stringstream ss;
for (unsigned int i = 0; i < VImageDimension; ++i)
{
ss << mmPerPixel[i];
if (i < VImageDimension -1)
ss << ",";
}
MITK_DEBUG << "Update convolution kernel for spacing (" << ss.str() << ") and radius " << radiusInMM << "mm";
double radiusInMMSquared = radiusInMM * radiusInMM;
typedef itk::Image< float, VImageDimension > KernelImageType;
typename KernelImageType::Pointer convolutionKernel = KernelImageType::New();
// Calculate size and allocate mask image
typedef typename KernelImageType::SizeType SizeType;
SizeType maskSize = this->CalculateConvolutionKernelSize<VImageDimension>(mmPerPixel, radiusInMM);
mitk::Point3D convolutionMaskCenterIndex;
convolutionMaskCenterIndex.Fill(0.0);
for(unsigned int i = 0; i < VImageDimension; ++i)
{
convolutionMaskCenterIndex[i] = 0.5 * (double)(maskSize[i]-1);
}
typedef typename KernelImageType::IndexType IndexType;
IndexType maskIndex;
maskIndex.Fill(0);
typedef typename KernelImageType::RegionType RegionType;
RegionType maskRegion;
maskRegion.SetSize(maskSize);
maskRegion.SetIndex(maskIndex);
convolutionKernel->SetRegions(maskRegion);
convolutionKernel->SetSpacing(mmPerPixel);
convolutionKernel->Allocate();
// Fill mask image values by subsampling the image grid
typedef itk::ImageRegionIteratorWithIndex<KernelImageType> MaskIteratorType;
MaskIteratorType maskIt(convolutionKernel,maskRegion);
int numberOfSubVoxelsPerDimension = 2; // per dimension!
int numberOfSubVoxels = ::pow( static_cast<float>(numberOfSubVoxelsPerDimension), static_cast<float>(VImageDimension) );
double subVoxelSizeInPixels = 1.0 / (double)numberOfSubVoxelsPerDimension;
double valueOfOneSubVoxel = 1.0 / (double)numberOfSubVoxels;
mitk::Point3D subVoxelIndexPosition;
double distanceSquared = 0.0;
typedef itk::ContinuousIndex<double, VImageDimension> ContinuousIndexType;
for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt)
{
ContinuousIndexType indexPoint(maskIt.GetIndex());
mitk::Point3D voxelPosition;
for (unsigned int dimension = 0; dimension < VImageDimension; ++dimension)
{
voxelPosition[dimension] = indexPoint[dimension];
}
double maskValue = 0.0;
mitk::Vector3D subVoxelOffset; subVoxelOffset.Fill(0.0);
// iterate sub-voxels by iterating all possible offsets
for (subVoxelOffset[0] = -0.5 + subVoxelSizeInPixels / 2.0;
subVoxelOffset[0] < +0.5;
subVoxelOffset[0] += subVoxelSizeInPixels)
{
for (subVoxelOffset[1] = -0.5 + subVoxelSizeInPixels / 2.0;
subVoxelOffset[1] < +0.5;
subVoxelOffset[1] += subVoxelSizeInPixels)
{
for (subVoxelOffset[2] = -0.5 + subVoxelSizeInPixels / 2.0;
subVoxelOffset[2] < +0.5;
subVoxelOffset[2] += subVoxelSizeInPixels)
{
subVoxelIndexPosition = voxelPosition + subVoxelOffset; // this COULD be integrated into the for-loops if neccessary (add voxelPosition to initializer and end condition)
distanceSquared =
(subVoxelIndexPosition[0]-convolutionMaskCenterIndex[0]) * mmPerPixel[0] * (subVoxelIndexPosition[0]-convolutionMaskCenterIndex[0]) * mmPerPixel[0]
+ (subVoxelIndexPosition[1]-convolutionMaskCenterIndex[1]) * mmPerPixel[1] * (subVoxelIndexPosition[1]-convolutionMaskCenterIndex[1]) * mmPerPixel[1]
+ (subVoxelIndexPosition[2]-convolutionMaskCenterIndex[2]) * mmPerPixel[2] * (subVoxelIndexPosition[2]-convolutionMaskCenterIndex[2]) * mmPerPixel[2];
if (distanceSquared <= radiusInMMSquared)
{
maskValue += valueOfOneSubVoxel;
}
}
}
}
maskIt.Set( maskValue );
}
return convolutionKernel;
}
template <typename TPixel, unsigned int VImageDimension>
itk::SmartPointer<itk::Image<TPixel, VImageDimension> >
HotspotMaskGenerator::GenerateConvolutionImage( const itk::Image<TPixel, VImageDimension>* inputImage )
{
double mmPerPixel[VImageDimension];
for (unsigned int dimension = 0; dimension < VImageDimension; ++dimension)
{
mmPerPixel[dimension] = inputImage->GetSpacing()[dimension];
}
// update convolution kernel
typedef itk::Image< float, VImageDimension > KernelImageType;
typename KernelImageType::Pointer convolutionKernel = this->GenerateHotspotSearchConvolutionKernel<VImageDimension>(mmPerPixel, m_HotspotRadiusinMM);
// update convolution image
typedef itk::Image< TPixel, VImageDimension > InputImageType;
typedef itk::Image< TPixel, VImageDimension > ConvolutionImageType;
typedef itk::FFTConvolutionImageFilter<InputImageType,
KernelImageType,
ConvolutionImageType> ConvolutionFilterType;
typename ConvolutionFilterType::Pointer convolutionFilter = ConvolutionFilterType::New();
typedef itk::ConstantBoundaryCondition<InputImageType, InputImageType> BoundaryConditionType;
BoundaryConditionType boundaryCondition;
boundaryCondition.SetConstant(0.0);
if (m_HotspotMustBeCompletelyInsideImage)
{
// overwrite default boundary condition
convolutionFilter->SetBoundaryCondition(&boundaryCondition);
}
convolutionFilter->SetInput(inputImage);
convolutionFilter->SetKernelImage(convolutionKernel);
convolutionFilter->SetNormalize(true);
MITK_DEBUG << "Update Convolution image for hotspot search";
convolutionFilter->UpdateLargestPossibleRegion();
typename ConvolutionImageType::Pointer convolutionImage = convolutionFilter->GetOutput();
convolutionImage->SetSpacing( inputImage->GetSpacing() ); // only workaround because convolution filter seems to ignore spacing of input image
return convolutionImage;
}
template < typename TPixel, unsigned int VImageDimension>
void
HotspotMaskGenerator::FillHotspotMaskPixels( itk::Image<TPixel, VImageDimension>* maskImage,
itk::Point<double, VImageDimension> sphereCenter,
double sphereRadiusInMM )
{
typedef itk::Image< TPixel, VImageDimension > MaskImageType;
typedef itk::ImageRegionIteratorWithIndex<MaskImageType> MaskImageIteratorType;
MaskImageIteratorType maskIt(maskImage, maskImage->GetLargestPossibleRegion());
typename MaskImageType::IndexType maskIndex;
typename MaskImageType::PointType worldPosition;
// this is not very smart. I would rather use a 0 initialized mask (not the case here -> blame CalculateHotspotMask) and find the region where I need to iterate over, then iterate only over the small region
for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt)
{
maskIndex = maskIt.GetIndex();
maskImage->TransformIndexToPhysicalPoint(maskIndex, worldPosition);
maskIt.Set( worldPosition.EuclideanDistanceTo(sphereCenter) <= sphereRadiusInMM ? 1 : 0 );
}
}
template <typename TPixel, unsigned int VImageDimension>
void
HotspotMaskGenerator::CalculateHotspotMask(itk::Image<TPixel, VImageDimension>* inputImage,
typename itk::Image<unsigned short, VImageDimension>::Pointer maskImage,
unsigned int label)
{
typedef itk::Image< TPixel, VImageDimension > InputImageType;
typedef itk::Image< TPixel, VImageDimension > ConvolutionImageType;
typedef itk::Image< unsigned short, VImageDimension > MaskImageType;
typename ConvolutionImageType::Pointer convolutionImage = this->GenerateConvolutionImage(inputImage);
if (convolutionImage.IsNull())
{
MITK_ERROR << "Empty convolution image in CalculateHotspotStatistics(). We should never reach this state (logic error).";
throw std::logic_error("Empty convolution image in CalculateHotspotStatistics()");
}
// if mask image is not defined, create an image of the same size as inputImage and fill it with 1's
// there is maybe a better way to do this!?
if (maskImage == nullptr)
{
maskImage = MaskImageType::New();
typename MaskImageType::RegionType maskRegion = inputImage->GetLargestPossibleRegion();
typename MaskImageType::SpacingType maskSpacing = inputImage->GetSpacing();
typename MaskImageType::PointType maskOrigin = inputImage->GetOrigin();
typename MaskImageType::DirectionType maskDirection = inputImage->GetDirection();
maskImage->SetRegions(maskRegion);
maskImage->Allocate();
maskImage->SetOrigin(maskOrigin);
maskImage->SetSpacing(maskSpacing);
maskImage->SetDirection(maskDirection);
maskImage->FillBuffer(1);
label = 1;
}
// find maximum in convolution image, given the current mask
double requiredDistanceToBorder = m_HotspotMustBeCompletelyInsideImage ? m_HotspotRadiusinMM : -1.0;
ImageExtrema convolutionImageInformation = CalculateExtremaWorld(convolutionImage.GetPointer(), maskImage, requiredDistanceToBorder, label);
bool isHotspotDefined = convolutionImageInformation.Defined;
if (!isHotspotDefined)
{
MITK_ERROR << "No origin of hotspot-sphere was calculated!";
m_InternalMask = nullptr;
}
else
{
// create a binary mask around the "hotspot" region, fill the shape of a sphere around our hotspot center
// typename DuplicatorType::Pointer copyMachine = DuplicatorType::New();
// copyMachine->SetInputImage(inputImage);
// copyMachine->Update();
// typename CastFilterType::Pointer caster = CastFilterType::New();
// caster->SetInput( copyMachine->GetOutput() );
// caster->Update();
typename MaskImageType::Pointer hotspotMaskITK = MaskImageType::New();
hotspotMaskITK->SetOrigin(inputImage->GetOrigin());
hotspotMaskITK->SetSpacing(inputImage->GetSpacing());
hotspotMaskITK->SetLargestPossibleRegion(inputImage->GetLargestPossibleRegion());
hotspotMaskITK->SetBufferedRegion(inputImage->GetBufferedRegion());
hotspotMaskITK->SetDirection(inputImage->GetDirection());
hotspotMaskITK->SetNumberOfComponentsPerPixel(inputImage->GetNumberOfComponentsPerPixel());
hotspotMaskITK->Allocate();
hotspotMaskITK->FillBuffer(1);
typedef typename InputImageType::IndexType IndexType;
IndexType maskCenterIndex;
for (unsigned int d =0; d< VImageDimension;++d)
{
maskCenterIndex[d]=convolutionImageInformation.MaxIndex[d];
}
typename ConvolutionImageType::PointType maskCenter;
inputImage->TransformIndexToPhysicalPoint(maskCenterIndex,maskCenter);
FillHotspotMaskPixels(hotspotMaskITK.GetPointer(), maskCenter, m_HotspotRadiusinMM);
//obtain mitk::Image::Pointer from itk::Image
mitk::Image::Pointer hotspotMaskAsMITKImage = mitk::GrabItkImageMemory(hotspotMaskITK);
m_InternalMask = hotspotMaskAsMITKImage;
m_ConvolutionImageMaxIndex = convolutionImageInformation.MaxIndex;
m_ConvolutionImageMinIndex = convolutionImageInformation.MinIndex;
}
}
bool HotspotMaskGenerator::IsUpdateRequired() const
{
unsigned long thisClassTimeStamp = this->GetMTime();
unsigned long internalMaskTimeStamp = m_InternalMask->GetMTime();
unsigned long maskGeneratorTimeStamp = m_Mask->GetMTime();
unsigned long inputImageTimeStamp = m_inputImage->GetMTime();
if (thisClassTimeStamp > m_InternalMaskUpdateTime) // inputs have changed
{
return true;
}
if (m_InternalMaskUpdateTime < maskGeneratorTimeStamp || m_InternalMaskUpdateTime < inputImageTimeStamp) // mask image has changed outside of this class
{
return true;
}
if (internalMaskTimeStamp > m_InternalMaskUpdateTime) // internal mask has been changed outside of this class
{
return true;
}
return false;
}
}
diff --git a/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp b/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp
index bfa5041c2f..45720f16a3 100644
--- a/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp
+++ b/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp
@@ -1,552 +1,548 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkImageStatisticsCalculator.h"
-#include <mitkExtendedLabelStatisticsImageFilter.h>
-#include <mitkExtendedStatisticsImageFilter.h>
+#include <mitkLabelStatisticsImageFilter.h>
+#include <mitkStatisticsImageFilter.h>
#include <mitkImage.h>
#include <mitkImageAccessByItk.h>
#include <mitkImageCast.h>
#include <mitkImageStatisticsConstants.h>
#include <mitkImageTimeSelector.h>
#include <mitkImageToItk.h>
#include <mitkMaskUtilities.h>
#include <mitkMinMaxImageFilterWithIndex.h>
#include <mitkMinMaxLabelmageFilterWithIndex.h>
#include <mitkitkMaskImageFilter.h>
namespace mitk
{
void ImageStatisticsCalculator::SetInputImage(const mitk::Image *image)
{
if (image != m_Image)
{
m_Image = image;
this->Modified();
}
}
void ImageStatisticsCalculator::SetMask(mitk::MaskGenerator *mask)
{
if (mask != m_MaskGenerator)
{
m_MaskGenerator = mask;
this->Modified();
}
}
void ImageStatisticsCalculator::SetSecondaryMask(mitk::MaskGenerator *mask)
{
if (mask != m_SecondaryMaskGenerator)
{
m_SecondaryMaskGenerator = mask;
this->Modified();
}
}
void ImageStatisticsCalculator::SetNBinsForHistogramStatistics(unsigned int nBins)
{
if (nBins != m_nBinsForHistogramStatistics)
{
m_nBinsForHistogramStatistics = nBins;
this->Modified();
this->m_UseBinSizeOverNBins = false;
}
if (m_UseBinSizeOverNBins)
{
this->Modified();
this->m_UseBinSizeOverNBins = false;
}
}
unsigned int ImageStatisticsCalculator::GetNBinsForHistogramStatistics() const
{
return m_nBinsForHistogramStatistics;
}
void ImageStatisticsCalculator::SetBinSizeForHistogramStatistics(double binSize)
{
if (binSize != m_binSizeForHistogramStatistics)
{
m_binSizeForHistogramStatistics = binSize;
this->Modified();
this->m_UseBinSizeOverNBins = true;
}
if (!m_UseBinSizeOverNBins)
{
this->Modified();
this->m_UseBinSizeOverNBins = true;
}
}
double ImageStatisticsCalculator::GetBinSizeForHistogramStatistics() const { return m_binSizeForHistogramStatistics; }
mitk::ImageStatisticsContainer* ImageStatisticsCalculator::GetStatistics(LabelIndex label)
{
if (m_Image.IsNull())
{
mitkThrow() << "no image";
}
if (!m_Image->IsInitialized())
{
mitkThrow() << "Image not initialized!";
}
if (IsUpdateRequired(label))
{
auto timeGeometry = m_Image->GetTimeGeometry();
// always compute statistics on all timesteps
for (unsigned int timeStep = 0; timeStep < m_Image->GetTimeSteps(); timeStep++)
{
if (m_MaskGenerator.IsNotNull())
{
m_MaskGenerator->SetTimeStep(timeStep);
//See T25625: otherwise, the mask is not computed again after setting a different time step
m_MaskGenerator->Modified();
m_InternalMask = m_MaskGenerator->GetMask();
if (m_MaskGenerator->GetReferenceImage().IsNotNull())
{
m_InternalImageForStatistics = m_MaskGenerator->GetReferenceImage();
}
else
{
m_InternalImageForStatistics = m_Image;
}
}
else
{
m_InternalImageForStatistics = m_Image;
}
if (m_SecondaryMaskGenerator.IsNotNull())
{
m_SecondaryMaskGenerator->SetTimeStep(timeStep);
m_SecondaryMask = m_SecondaryMaskGenerator->GetMask();
}
ImageTimeSelector::Pointer imgTimeSel = ImageTimeSelector::New();
imgTimeSel->SetInput(m_InternalImageForStatistics);
imgTimeSel->SetTimeNr(timeStep);
imgTimeSel->UpdateLargestPossibleRegion();
imgTimeSel->Update();
m_ImageTimeSlice = imgTimeSel->GetOutput();
// Calculate statistics with/without mask
if (m_MaskGenerator.IsNull() && m_SecondaryMaskGenerator.IsNull())
{
// 1) calculate statistics unmasked:
AccessByItk_2(m_ImageTimeSlice, InternalCalculateStatisticsUnmasked, timeGeometry, timeStep)
}
else
{
// 2) calculate statistics masked
AccessByItk_2(m_ImageTimeSlice, InternalCalculateStatisticsMasked, timeGeometry, timeStep)
}
}
}
auto it = m_StatisticContainers.find(label);
if (it != m_StatisticContainers.end())
{
return (it->second).GetPointer();
}
else
{
mitkThrow() << "unknown label";
return nullptr;
}
}
template <typename TPixel, unsigned int VImageDimension>
void ImageStatisticsCalculator::InternalCalculateStatisticsUnmasked(
typename itk::Image<TPixel, VImageDimension> *image, const TimeGeometry *timeGeometry, TimeStepType timeStep)
{
typedef typename itk::Image<TPixel, VImageDimension> ImageType;
- typedef typename itk::ExtendedStatisticsImageFilter<ImageType> ImageStatisticsFilterType;
+ typedef typename mitk::StatisticsImageFilter<ImageType> ImageStatisticsFilterType;
typedef typename itk::MinMaxImageFilterWithIndex<ImageType> MinMaxFilterType;
// reset statistics container if exists
ImageStatisticsContainer::Pointer statisticContainerForImage;
LabelIndex labelNoMask = 1;
auto it = m_StatisticContainers.find(labelNoMask);
if (it != m_StatisticContainers.end())
{
statisticContainerForImage = it->second;
}
else
{
statisticContainerForImage = ImageStatisticsContainer::New();
statisticContainerForImage->SetTimeGeometry(const_cast<mitk::TimeGeometry*>(timeGeometry));
m_StatisticContainers.emplace(labelNoMask, statisticContainerForImage);
}
auto statObj = ImageStatisticsContainer::ImageStatisticsObject();
typename ImageStatisticsFilterType::Pointer statisticsFilter = ImageStatisticsFilterType::New();
statisticsFilter->SetInput(image);
statisticsFilter->SetCoordinateTolerance(0.001);
statisticsFilter->SetDirectionTolerance(0.001);
// TODO: this is single threaded. Implement our own image filter that does this multi threaded
// typename itk::MinimumMaximumImageCalculator<ImageType>::Pointer imgMinMaxFilter =
// itk::MinimumMaximumImageCalculator<ImageType>::New(); imgMinMaxFilter->SetImage(image);
// imgMinMaxFilter->Compute();
vnl_vector<int> minIndex, maxIndex;
typename MinMaxFilterType::Pointer minMaxFilter = MinMaxFilterType::New();
minMaxFilter->SetInput(image);
minMaxFilter->UpdateLargestPossibleRegion();
typename ImageType::PixelType minval = minMaxFilter->GetMin();
typename ImageType::PixelType maxval = minMaxFilter->GetMax();
typename ImageType::IndexType tmpMinIndex = minMaxFilter->GetMinIndex();
typename ImageType::IndexType tmpMaxIndex = minMaxFilter->GetMaxIndex();
// typename ImageType::IndexType tmpMinIndex = imgMinMaxFilter->GetIndexOfMinimum();
// typename ImageType::IndexType tmpMaxIndex = imgMinMaxFilter->GetIndexOfMaximum();
minIndex.set_size(tmpMaxIndex.GetIndexDimension());
maxIndex.set_size(tmpMaxIndex.GetIndexDimension());
for (unsigned int i = 0; i < tmpMaxIndex.GetIndexDimension(); i++)
{
minIndex[i] = tmpMinIndex[i];
maxIndex[i] = tmpMaxIndex[i];
}
statObj.AddStatistic(mitk::ImageStatisticsConstants::MINIMUMPOSITION(), minIndex);
statObj.AddStatistic(mitk::ImageStatisticsConstants::MAXIMUMPOSITION(), maxIndex);
// convert m_binSize in m_nBins if necessary
unsigned int nBinsForHistogram;
if (m_UseBinSizeOverNBins)
{
nBinsForHistogram = std::max(static_cast<double>(std::ceil(maxval - minval)) / m_binSizeForHistogramStatistics,
10.); // do not allow less than 10 bins
}
else
{
nBinsForHistogram = m_nBinsForHistogramStatistics;
}
statisticsFilter->SetHistogramParameters(nBinsForHistogram, minval, maxval);
try
{
statisticsFilter->Update();
}
catch (const itk::ExceptionObject &e)
{
mitkThrow() << "Image statistics calculation failed due to following ITK Exception: \n " << e.what();
}
auto voxelVolume = GetVoxelVolume<TPixel, VImageDimension>(image);
auto numberOfPixels = image->GetLargestPossibleRegion().GetNumberOfPixels();
auto volume = static_cast<double>(numberOfPixels) * voxelVolume;
auto variance = statisticsFilter->GetSigma() * statisticsFilter->GetSigma();
auto rms =
std::sqrt(std::pow(statisticsFilter->GetMean(), 2.) + statisticsFilter->GetVariance()); // variance = sigma^2
statObj.AddStatistic(mitk::ImageStatisticsConstants::NUMBEROFVOXELS(),
static_cast<ImageStatisticsContainer::VoxelCountType>(numberOfPixels));
statObj.AddStatistic(mitk::ImageStatisticsConstants::VOLUME(), volume);
statObj.AddStatistic(mitk::ImageStatisticsConstants::MEAN(), statisticsFilter->GetMean());
statObj.AddStatistic(mitk::ImageStatisticsConstants::MINIMUM(),
static_cast<ImageStatisticsContainer::RealType>(statisticsFilter->GetMinimum()));
statObj.AddStatistic(mitk::ImageStatisticsConstants::MAXIMUM(),
static_cast<ImageStatisticsContainer::RealType>(statisticsFilter->GetMaximum()));
statObj.AddStatistic(mitk::ImageStatisticsConstants::STANDARDDEVIATION(), statisticsFilter->GetSigma());
statObj.AddStatistic(mitk::ImageStatisticsConstants::VARIANCE(), variance);
statObj.AddStatistic(mitk::ImageStatisticsConstants::SKEWNESS(), statisticsFilter->GetSkewness());
statObj.AddStatistic(mitk::ImageStatisticsConstants::KURTOSIS(), statisticsFilter->GetKurtosis());
statObj.AddStatistic(mitk::ImageStatisticsConstants::RMS(), rms);
statObj.AddStatistic(mitk::ImageStatisticsConstants::MPP(), statisticsFilter->GetMPP());
statObj.AddStatistic(mitk::ImageStatisticsConstants::ENTROPY(), statisticsFilter->GetEntropy());
statObj.AddStatistic(mitk::ImageStatisticsConstants::MEDIAN(), statisticsFilter->GetMedian());
statObj.AddStatistic(mitk::ImageStatisticsConstants::UNIFORMITY(), statisticsFilter->GetUniformity());
statObj.AddStatistic(mitk::ImageStatisticsConstants::UPP(), statisticsFilter->GetUPP());
- statObj.m_Histogram = statisticsFilter->GetHistogram().GetPointer();
+ statObj.m_Histogram = statisticsFilter->GetHistogram();
statisticContainerForImage->SetStatisticsForTimeStep(timeStep, statObj);
}
template <typename TPixel, unsigned int VImageDimension>
double ImageStatisticsCalculator::GetVoxelVolume(typename itk::Image<TPixel, VImageDimension> *image) const
{
auto spacing = image->GetSpacing();
double voxelVolume = 1.;
for (unsigned int i = 0; i < image->GetImageDimension(); i++)
{
voxelVolume *= spacing[i];
}
return voxelVolume;
}
template <typename TPixel, unsigned int VImageDimension>
void ImageStatisticsCalculator::InternalCalculateStatisticsMasked(typename itk::Image<TPixel, VImageDimension> *image,
const TimeGeometry *timeGeometry,
unsigned int timeStep)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::Image<MaskPixelType, VImageDimension> MaskType;
typedef typename MaskType::PixelType LabelPixelType;
- typedef itk::ExtendedLabelStatisticsImageFilter<ImageType, MaskType> ImageStatisticsFilterType;
+ typedef LabelStatisticsImageFilter<ImageType> ImageStatisticsFilterType;
typedef MaskUtilities<TPixel, VImageDimension> MaskUtilType;
typedef typename itk::MinMaxLabelImageFilterWithIndex<ImageType, MaskType> MinMaxLabelFilterType;
- typedef typename ImageType::PixelType InputImgPixelType;
// workaround: if m_SecondaryMaskGenerator ist not null but m_MaskGenerator is! (this is the case if we request a
// 'ignore zuero valued pixels' mask in the gui but do not define a primary mask)
bool swapMasks = false;
if (m_SecondaryMask.IsNotNull() && m_InternalMask.IsNull())
{
m_InternalMask = m_SecondaryMask;
m_SecondaryMask = nullptr;
swapMasks = true;
}
// maskImage has to have the same dimension as image
typename MaskType::Pointer maskImage = MaskType::New();
try
{
// try to access the pixel values directly (no copying or casting). Only works if mask pixels are of pixelType
// unsigned short
maskImage = ImageToItkImage<MaskPixelType, VImageDimension>(m_InternalMask);
}
catch (const itk::ExceptionObject &)
{
// if the pixel type of the mask is not short, then we have to make a copy of m_InternalMask (and cast the values)
CastToItkImage(m_InternalMask, maskImage);
}
// if we have a secondary mask (say a ignoreZeroPixelMask) we need to combine the masks (corresponds to AND)
if (m_SecondaryMask.IsNotNull())
{
// dirty workaround for a bug when pf mask + any other mask is used in conjunction. We need a proper fix for this
// (Fabian Isensee is responsible and probably working on it!)
if (m_InternalMask->GetDimension() == 2 &&
(m_SecondaryMask->GetDimension() == 3 || m_SecondaryMask->GetDimension() == 4))
{
mitk::Image::ConstPointer old_img = m_SecondaryMaskGenerator->GetReferenceImage();
m_SecondaryMaskGenerator->SetInputImage(m_MaskGenerator->GetReferenceImage());
m_SecondaryMask = m_SecondaryMaskGenerator->GetMask();
m_SecondaryMaskGenerator->SetInputImage(old_img);
}
typename MaskType::Pointer secondaryMaskImage = MaskType::New();
secondaryMaskImage = ImageToItkImage<MaskPixelType, VImageDimension>(m_SecondaryMask);
// secondary mask should be a ignore zero value pixel mask derived from image. it has to be cropped to the mask
// region (which may be planar or simply smaller)
typename MaskUtilities<MaskPixelType, VImageDimension>::Pointer secondaryMaskMaskUtil =
MaskUtilities<MaskPixelType, VImageDimension>::New();
secondaryMaskMaskUtil->SetImage(secondaryMaskImage.GetPointer());
secondaryMaskMaskUtil->SetMask(maskImage.GetPointer());
typename MaskType::Pointer adaptedSecondaryMaskImage = secondaryMaskMaskUtil->ExtractMaskImageRegion();
typename itk::MaskImageFilter2<MaskType, MaskType, MaskType>::Pointer maskFilter =
itk::MaskImageFilter2<MaskType, MaskType, MaskType>::New();
maskFilter->SetInput1(maskImage);
maskFilter->SetInput2(adaptedSecondaryMaskImage);
maskFilter->SetMaskingValue(
1); // all pixels of maskImage where secondaryMaskImage==1 will be kept, all the others are set to 0
maskFilter->UpdateLargestPossibleRegion();
maskImage = maskFilter->GetOutput();
}
typename MaskUtilType::Pointer maskUtil = MaskUtilType::New();
maskUtil->SetImage(image);
maskUtil->SetMask(maskImage.GetPointer());
// if mask is smaller than image, extract the image region where the mask is
typename ImageType::Pointer adaptedImage = ImageType::New();
adaptedImage = maskUtil->ExtractMaskImageRegion(); // this also checks mask sanity
// find min, max, minindex and maxindex
typename MinMaxLabelFilterType::Pointer minMaxFilter = MinMaxLabelFilterType::New();
minMaxFilter->SetInput(adaptedImage);
minMaxFilter->SetLabelInput(maskImage);
minMaxFilter->UpdateLargestPossibleRegion();
// set histogram parameters for each label individually (min/max may be different for each label)
- typedef typename std::map<LabelPixelType, InputImgPixelType> MapType;
- typedef typename std::pair<LabelPixelType, InputImgPixelType> PairType;
+ typedef typename std::unordered_map<LabelPixelType, ScalarType> MapType;
std::vector<LabelPixelType> relevantLabels = minMaxFilter->GetRelevantLabels();
MapType minVals;
MapType maxVals;
- std::map<LabelPixelType, unsigned int> nBins;
+ std::unordered_map<LabelPixelType, unsigned int> nBins;
for (LabelPixelType label : relevantLabels)
{
- minVals.insert(PairType(label, minMaxFilter->GetMin(label)));
- maxVals.insert(PairType(label, minMaxFilter->GetMax(label)));
+ minVals[label] = static_cast<ScalarType>(minMaxFilter->GetMin(label));
+ maxVals[label] = static_cast<ScalarType>(minMaxFilter->GetMax(label));
unsigned int nBinsForHistogram;
if (m_UseBinSizeOverNBins)
{
nBinsForHistogram =
std::max(static_cast<double>(std::ceil(minMaxFilter->GetMax(label) - minMaxFilter->GetMin(label))) /
m_binSizeForHistogramStatistics,
10.); // do not allow less than 10 bins
}
else
{
nBinsForHistogram = m_nBinsForHistogramStatistics;
}
- nBins.insert(typename std::pair<LabelPixelType, unsigned int>(label, nBinsForHistogram));
+ nBins[label] = nBinsForHistogram;
}
typename ImageStatisticsFilterType::Pointer imageStatisticsFilter = ImageStatisticsFilterType::New();
imageStatisticsFilter->SetDirectionTolerance(0.001);
imageStatisticsFilter->SetCoordinateTolerance(0.001);
imageStatisticsFilter->SetInput(adaptedImage);
imageStatisticsFilter->SetLabelInput(maskImage);
- imageStatisticsFilter->SetHistogramParametersForLabels(nBins, minVals, maxVals);
+ imageStatisticsFilter->SetHistogramParameters(nBins, minVals, maxVals);
imageStatisticsFilter->Update();
- std::list<int> labels = imageStatisticsFilter->GetRelevantLabels();
+ auto labels = imageStatisticsFilter->GetValidLabelValues();
auto it = labels.begin();
while (it != labels.end())
{
ImageStatisticsContainer::Pointer statisticContainerForLabelImage;
auto labelIt = m_StatisticContainers.find(*it);
// reset if statisticContainer already exist
if (labelIt != m_StatisticContainers.end())
{
statisticContainerForLabelImage = labelIt->second;
}
// create new statisticContainer
else
{
statisticContainerForLabelImage = ImageStatisticsContainer::New();
statisticContainerForLabelImage->SetTimeGeometry(const_cast<mitk::TimeGeometry*>(timeGeometry));
// link label (*it) to statisticContainer
m_StatisticContainers.emplace(*it, statisticContainerForLabelImage);
}
ImageStatisticsContainer::ImageStatisticsObject statObj;
// find min, max, minindex and maxindex
// make sure to only look in the masked region, use a masker for this
vnl_vector<int> minIndex, maxIndex;
mitk::Point3D worldCoordinateMin;
mitk::Point3D worldCoordinateMax;
mitk::Point3D indexCoordinateMin;
mitk::Point3D indexCoordinateMax;
m_InternalImageForStatistics->GetGeometry()->IndexToWorld(minMaxFilter->GetMinIndex(*it), worldCoordinateMin);
m_InternalImageForStatistics->GetGeometry()->IndexToWorld(minMaxFilter->GetMaxIndex(*it), worldCoordinateMax);
m_Image->GetGeometry()->WorldToIndex(worldCoordinateMin, indexCoordinateMin);
m_Image->GetGeometry()->WorldToIndex(worldCoordinateMax, indexCoordinateMax);
minIndex.set_size(3);
maxIndex.set_size(3);
// for (unsigned int i=0; i < tmpMaxIndex.GetIndexDimension(); i++)
for (unsigned int i = 0; i < 3; i++)
{
minIndex[i] = indexCoordinateMin[i];
maxIndex[i] = indexCoordinateMax[i];
}
statObj.AddStatistic(mitk::ImageStatisticsConstants::MINIMUMPOSITION(), minIndex);
statObj.AddStatistic(mitk::ImageStatisticsConstants::MAXIMUMPOSITION(), maxIndex);
- assert(std::abs(minMaxFilter->GetMax(*it) - imageStatisticsFilter->GetMaximum(*it)) < mitk::eps);
- assert(std::abs(minMaxFilter->GetMin(*it) - imageStatisticsFilter->GetMinimum(*it)) < mitk::eps);
-
auto voxelVolume = GetVoxelVolume<TPixel, VImageDimension>(image);
auto numberOfVoxels =
static_cast<unsigned long>(imageStatisticsFilter->GetCount(*it));
auto volume = static_cast<double>(numberOfVoxels) * voxelVolume;
auto rms = std::sqrt(std::pow(imageStatisticsFilter->GetMean(*it), 2.) +
imageStatisticsFilter->GetVariance(*it)); // variance = sigma^2
auto variance = imageStatisticsFilter->GetSigma(*it) * imageStatisticsFilter->GetSigma(*it);
statObj.AddStatistic(mitk::ImageStatisticsConstants::NUMBEROFVOXELS(), numberOfVoxels);
statObj.AddStatistic(mitk::ImageStatisticsConstants::VOLUME(), volume);
statObj.AddStatistic(mitk::ImageStatisticsConstants::MEAN(), imageStatisticsFilter->GetMean(*it));
- statObj.AddStatistic(mitk::ImageStatisticsConstants::MINIMUM(), imageStatisticsFilter->GetMinimum(*it));
- statObj.AddStatistic(mitk::ImageStatisticsConstants::MAXIMUM(), imageStatisticsFilter->GetMaximum(*it));
+ statObj.AddStatistic(mitk::ImageStatisticsConstants::MINIMUM(),
+ static_cast<ImageStatisticsContainer::RealType>(imageStatisticsFilter->GetMinimum(*it)));
+ statObj.AddStatistic(mitk::ImageStatisticsConstants::MAXIMUM(),
+ static_cast<ImageStatisticsContainer::RealType>(imageStatisticsFilter->GetMaximum(*it)));
statObj.AddStatistic(mitk::ImageStatisticsConstants::STANDARDDEVIATION(), imageStatisticsFilter->GetSigma(*it));
statObj.AddStatistic(mitk::ImageStatisticsConstants::VARIANCE(), variance);
statObj.AddStatistic(mitk::ImageStatisticsConstants::SKEWNESS(), imageStatisticsFilter->GetSkewness(*it));
statObj.AddStatistic(mitk::ImageStatisticsConstants::KURTOSIS(), imageStatisticsFilter->GetKurtosis(*it));
statObj.AddStatistic(mitk::ImageStatisticsConstants::RMS(), rms);
statObj.AddStatistic(mitk::ImageStatisticsConstants::MPP(), imageStatisticsFilter->GetMPP(*it));
statObj.AddStatistic(mitk::ImageStatisticsConstants::ENTROPY(), imageStatisticsFilter->GetEntropy(*it));
statObj.AddStatistic(mitk::ImageStatisticsConstants::MEDIAN(), imageStatisticsFilter->GetMedian(*it));
statObj.AddStatistic(mitk::ImageStatisticsConstants::UNIFORMITY(), imageStatisticsFilter->GetUniformity(*it));
statObj.AddStatistic(mitk::ImageStatisticsConstants::UPP(), imageStatisticsFilter->GetUPP(*it));
- statObj.m_Histogram = imageStatisticsFilter->GetHistogram(*it).GetPointer();
-
+ statObj.m_Histogram = imageStatisticsFilter->GetHistogram(*it);
statisticContainerForLabelImage->SetStatisticsForTimeStep(timeStep, statObj);
++it;
}
// swap maskGenerators back
if (swapMasks)
{
m_SecondaryMask = m_InternalMask;
m_InternalMask = nullptr;
}
}
bool ImageStatisticsCalculator::IsUpdateRequired(LabelIndex label) const
{
unsigned long thisClassTimeStamp = this->GetMTime();
unsigned long inputImageTimeStamp = m_Image->GetMTime();
auto it = m_StatisticContainers.find(label);
if (it == m_StatisticContainers.end())
{
return true;
}
unsigned long statisticsTimeStamp = it->second->GetMTime();
if (thisClassTimeStamp > statisticsTimeStamp) // inputs have changed
{
return true;
}
if (inputImageTimeStamp > statisticsTimeStamp) // image has changed
{
return true;
}
if (m_MaskGenerator.IsNotNull())
{
unsigned long maskGeneratorTimeStamp = m_MaskGenerator->GetMTime();
if (maskGeneratorTimeStamp > statisticsTimeStamp) // there is a mask generator and it has changed
{
return true;
}
}
if (m_SecondaryMaskGenerator.IsNotNull())
{
unsigned long maskGeneratorTimeStamp = m_SecondaryMaskGenerator->GetMTime();
if (maskGeneratorTimeStamp > statisticsTimeStamp) // there is a secondary mask generator and it has changed
{
return true;
}
}
return false;
}
} // namespace mitk
diff --git a/Modules/ImageStatistics/mitkLabelStatisticsImageFilter.h b/Modules/ImageStatistics/mitkLabelStatisticsImageFilter.h
new file mode 100644
index 0000000000..817418e2ce
--- /dev/null
+++ b/Modules/ImageStatistics/mitkLabelStatisticsImageFilter.h
@@ -0,0 +1,169 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+// This file is based on ITK's itkLabelStatisticsImageFilter.h
+
+#ifndef mitkLabelStatisticsImageFilter_h
+#define mitkLabelStatisticsImageFilter_h
+
+#include <itkCompensatedSummation.h>
+#include <itkHistogram.h>
+#include <itkImageSink.h>
+#include <itkNumericTraits.h>
+#include <itkSimpleDataObjectDecorator.h>
+
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+
+#include <mitkLabel.h>
+
+namespace mitk
+{
+ template <typename TInputImage>
+ class LabelStatisticsImageFilter : public itk::ImageSink<TInputImage>
+ {
+ public:
+ using Self = LabelStatisticsImageFilter;
+ using Superclass = itk::ImageSink<TInputImage>;
+ using Pointer = itk::SmartPointer<Self>;
+ using ConstPointer = itk::SmartPointer<const Self>;
+
+ itkFactorylessNewMacro(Self);
+
+ itkTypeMacro(LabelStatisticsImageFilter, itk::ImageSink);
+
+ using IndexType = typename TInputImage::IndexType;
+ using SizeType = typename TInputImage::SizeType;
+ using RegionType = typename TInputImage::RegionType;
+ using PixelType = typename TInputImage::PixelType;
+ using LabelPixelType = typename mitk::Label::PixelType;
+
+ static constexpr unsigned int ImageDimension = TInputImage::ImageDimension;
+
+ using RealType = typename itk::NumericTraits<PixelType>::RealType;
+
+ using DataObjectPointer = typename itk::DataObject::Pointer;
+
+ using RealObjectType = itk::SimpleDataObjectDecorator<RealType>;
+
+ using BoundingBoxType = std::vector<itk::IndexValueType>;
+
+ using HistogramType = itk::Statistics::Histogram<RealType>;
+ using HistogramPointer = typename HistogramType::Pointer;
+
+ class LabelStatistics
+ {
+ public:
+ LabelStatistics();
+ LabelStatistics(unsigned int size, RealType lowerBound, RealType upperBound);
+ ~LabelStatistics();
+
+ itk::SizeValueType m_Count;
+ itk::SizeValueType m_CountOfPositivePixels;
+ RealType m_Min;
+ RealType m_Max;
+ RealType m_Mean;
+ itk::CompensatedSummation<RealType> m_Sum;
+ itk::CompensatedSummation<RealType> m_SumOfPositivePixels;
+ itk::CompensatedSummation<RealType> m_SumOfSquares;
+ itk::CompensatedSummation<RealType> m_SumOfCubes;
+ itk::CompensatedSummation<RealType> m_SumOfQuadruples;
+ RealType m_Sigma;
+ RealType m_Variance;
+ RealType m_MPP;
+ RealType m_Median;
+ RealType m_Uniformity;
+ RealType m_UPP;
+ RealType m_Entropy;
+ RealType m_Skewness;
+ RealType m_Kurtosis;
+ BoundingBoxType m_BoundingBox;
+ HistogramPointer m_Histogram;
+ };
+
+ using MapType = std::unordered_map<LabelPixelType, LabelStatistics>;
+ using MapIterator = typename MapType::iterator;
+ using MapConstIterator = typename MapType::const_iterator;
+
+ using ValidLabelValuesContainerType = std::vector<LabelPixelType>;
+ const ValidLabelValuesContainerType& GetValidLabelValues() const;
+
+ void SetHistogramParameters(
+ const std::unordered_map<LabelPixelType, unsigned int>& sizes,
+ const std::unordered_map<LabelPixelType, RealType>& lowerBounds,
+ const std::unordered_map<LabelPixelType, RealType>& upperBounds);
+
+ using LabelImageType = itk::Image<LabelPixelType, ImageDimension>;
+ using ProcessObject = itk::ProcessObject;
+
+ itkSetInputMacro(LabelInput, LabelImageType);
+ itkGetInputMacro(LabelInput, LabelImageType);
+
+ bool HasLabel(LabelPixelType label) const;
+ unsigned int GetNumberOfObjects() const;
+ unsigned int GetNumberOfLabels() const;
+
+ PixelType GetMinimum(LabelPixelType label) const;
+ PixelType GetMaximum(LabelPixelType label) const;
+ RealType GetMean(LabelPixelType label) const;
+ RealType GetSigma(LabelPixelType label) const;
+ RealType GetVariance(LabelPixelType label) const;
+ BoundingBoxType GetBoundingBox(LabelPixelType label) const;
+ RegionType GetRegion(LabelPixelType label) const;
+ RealType GetSum(LabelPixelType label) const;
+ RealType GetSumOfSquares(LabelPixelType label) const;
+ RealType GetSumOfCubes(LabelPixelType label) const;
+ RealType GetSumOfQuadruples(LabelPixelType label) const;
+ RealType GetSkewness(LabelPixelType label) const;
+ RealType GetKurtosis(LabelPixelType label) const;
+ RealType GetMPP(LabelPixelType label) const;
+ itk::SizeValueType GetCount(LabelPixelType label) const;
+ HistogramPointer GetHistogram(LabelPixelType label) const;
+ RealType GetEntropy(LabelPixelType label) const;
+ RealType GetUniformity(LabelPixelType label) const;
+ RealType GetUPP(LabelPixelType label) const;
+ RealType GetMedian(LabelPixelType label) const;
+
+ protected:
+ LabelStatisticsImageFilter();
+ ~LabelStatisticsImageFilter();
+
+ void BeforeStreamedGenerateData() override;
+ void ThreadedStreamedGenerateData(const RegionType&) override;
+ void AfterStreamedGenerateData() override;
+
+ void PrintSelf(std::ostream& os, itk::Indent indent) const override;
+
+ private:
+ const LabelStatistics& GetLabelStatistics(LabelPixelType label) const;
+ const LabelStatistics& GetLabelHistogramStatistics(LabelPixelType label) const;
+
+ void MergeMap(MapType& map1, MapType& map2) const;
+
+ MapType m_LabelStatistics;
+ ValidLabelValuesContainerType m_ValidLabelValues;
+
+ bool m_ComputeHistograms;
+ std::unordered_map<LabelPixelType, unsigned int> m_HistogramSizes;
+ std::unordered_map<LabelPixelType, RealType> m_HistogramLowerBounds;
+ std::unordered_map<LabelPixelType, RealType> m_HistogramUpperBounds;
+
+ std::mutex m_Mutex;
+ };
+}
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include <mitkLabelStatisticsImageFilter.hxx>
+#endif
+
+#endif
diff --git a/Modules/ImageStatistics/mitkLabelStatisticsImageFilter.hxx b/Modules/ImageStatistics/mitkLabelStatisticsImageFilter.hxx
new file mode 100644
index 0000000000..e5901c97c8
--- /dev/null
+++ b/Modules/ImageStatistics/mitkLabelStatisticsImageFilter.hxx
@@ -0,0 +1,544 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#ifndef mitkLabelStatisticsImageFilter_hxx
+#define mitkLabelStatisticsImageFilter_hxx
+
+#include "mitkLabelStatisticsImageFilter.h"
+
+#include <mitkHistogramStatisticsCalculator.h>
+
+#include <itkImageLinearConstIteratorWithIndex.h>
+#include <itkImageScanlineConstIterator.h>
+
+template <typename TInputImage>
+mitk::LabelStatisticsImageFilter<TInputImage>::LabelStatistics::LabelStatistics()
+ : m_Count(0),
+ m_CountOfPositivePixels(0),
+ m_Min(itk::NumericTraits<RealType>::max()),
+ m_Max(itk::NumericTraits<RealType>::NonpositiveMin()),
+ m_Mean(0),
+ m_Sum(0),
+ m_SumOfPositivePixels(0),
+ m_SumOfSquares(0),
+ m_SumOfCubes(0),
+ m_SumOfQuadruples(0),
+ m_Sigma(0),
+ m_Variance(0),
+ m_MPP(0),
+ m_Median(0),
+ m_Uniformity(0),
+ m_UPP(0),
+ m_Entropy(0),
+ m_Skewness(0),
+ m_Kurtosis(0)
+{
+ m_BoundingBox.resize(ImageDimension * 2);
+
+ for (std::remove_const_t<decltype(ImageDimension)> i = 0; i < ImageDimension * 2; i += 2)
+ {
+ m_BoundingBox[i] = itk::NumericTraits<itk::IndexValueType>::max();
+ m_BoundingBox[i + 1] = itk::NumericTraits<itk::IndexValueType>::NonpositiveMin();
+ }
+}
+
+template <typename TInputImage>
+mitk::LabelStatisticsImageFilter<TInputImage>::LabelStatistics::LabelStatistics(unsigned int size, RealType lowerBound, RealType upperBound)
+ : LabelStatistics()
+{
+ typename HistogramType::SizeType histogramSize;
+ histogramSize.SetSize(1);
+ histogramSize[0] = size;
+
+ typename HistogramType::MeasurementVectorType histogramLowerBound;
+ histogramLowerBound.SetSize(1);
+ histogramLowerBound[0] = lowerBound;
+
+ typename HistogramType::MeasurementVectorType histogramUpperBound;
+ histogramUpperBound.SetSize(1);
+ histogramUpperBound[0] = upperBound;
+
+ m_Histogram = HistogramType::New();
+ m_Histogram->SetMeasurementVectorSize(1);
+ m_Histogram->Initialize(histogramSize, histogramLowerBound, histogramUpperBound);
+}
+
+template <typename TInputImage>
+mitk::LabelStatisticsImageFilter<TInputImage>::LabelStatistics::~LabelStatistics()
+{
+}
+
+template <typename TInputImage>
+mitk::LabelStatisticsImageFilter<TInputImage>::LabelStatisticsImageFilter()
+ : m_ComputeHistograms(false)
+{
+ this->AddRequiredInputName("LabelInput");
+}
+
+template <typename TInputImage>
+mitk::LabelStatisticsImageFilter<TInputImage>::~LabelStatisticsImageFilter()
+{
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::BeforeStreamedGenerateData() -> void
+{
+ this->AllocateOutputs();
+ m_LabelStatistics.clear();
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::ThreadedStreamedGenerateData(const RegionType& region) -> void
+{
+ if (0 == region.GetSize(0))
+ return;
+
+ MapType localStats;
+
+ typename HistogramType::MeasurementVectorType histogramMeasurement(1);
+ typename HistogramType::IndexType histogramIndex(1);
+
+ using TLabelImage = itk::Image<LabelPixelType, ImageDimension>;
+
+ itk::ImageLinearConstIteratorWithIndex<TInputImage> it(this->GetInput(), region);
+ itk::ImageScanlineConstIterator<TLabelImage> labelIt(this->GetLabelInput(), region);
+
+ auto mapIt = localStats.end();
+
+ while (!it.IsAtEnd())
+ {
+ while (!it.IsAtEndOfLine())
+ {
+ const auto& value = static_cast<RealType>(it.Get());
+ const auto& index = it.GetIndex();
+ const auto& label = labelIt.Get();
+
+ mapIt = localStats.find(label);
+
+ if (mapIt == localStats.end())
+ {
+ mapIt = m_ComputeHistograms
+ ? localStats.emplace(label, LabelStatistics(m_HistogramSizes[label], m_HistogramLowerBounds[label], m_HistogramUpperBounds[label])).first
+ : localStats.emplace(label, LabelStatistics()).first;
+ }
+
+ auto& labelStats = mapIt->second;
+
+ labelStats.m_Min = std::min(labelStats.m_Min, value);
+ labelStats.m_Max = std::max(labelStats.m_Max, value);
+ labelStats.m_Sum += value;
+ auto squareValue = value * value;
+ labelStats.m_SumOfSquares += squareValue;
+ labelStats.m_SumOfCubes += squareValue * value;
+ labelStats.m_SumOfQuadruples += squareValue * squareValue;
+ ++labelStats.m_Count;
+
+ if (0 < value)
+ {
+ labelStats.m_SumOfPositivePixels += value;
+ ++labelStats.m_CountOfPositivePixels;
+ }
+
+ for (unsigned int i = 0; i < (ImageDimension * 2); i += 2)
+ {
+ labelStats.m_BoundingBox[i] = std::min(labelStats.m_BoundingBox[i], index[i / 2]);
+ labelStats.m_BoundingBox[i + 1] = std::max(labelStats.m_BoundingBox[i + 1], index[i / 2]);
+ }
+
+ if (m_ComputeHistograms)
+ {
+ histogramMeasurement[0] = value;
+ labelStats.m_Histogram->GetIndex(histogramMeasurement, histogramIndex);
+ labelStats.m_Histogram->IncreaseFrequencyOfIndex(histogramIndex, 1);
+ }
+
+ ++labelIt;
+ ++it;
+ }
+
+ labelIt.NextLine();
+ it.NextLine();
+ }
+
+ // Merge localStats and m_LabelStatistics concurrently safe in a local copy
+
+ while (true)
+ {
+ std::unique_lock<std::mutex> lock(m_Mutex);
+
+ if (m_LabelStatistics.empty())
+ {
+ std::swap(m_LabelStatistics, localStats);
+ break;
+ }
+ else
+ {
+ MapType toMerge;
+ std::swap(m_LabelStatistics, toMerge);
+ lock.unlock();
+ this->MergeMap(localStats, toMerge);
+ }
+ }
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::AfterStreamedGenerateData() -> void
+{
+ Superclass::AfterStreamedGenerateData();
+
+ m_ValidLabelValues.clear();
+ m_ValidLabelValues.reserve(m_LabelStatistics.size());
+
+ for (auto& val : m_LabelStatistics)
+ {
+ m_ValidLabelValues.push_back(val.first);
+ auto& stats = val.second;
+
+ const auto& sum = stats.m_Sum.GetSum();
+ const auto& sumOfSquares = stats.m_SumOfSquares.GetSum();
+ const auto& sumOfCubes = stats.m_SumOfCubes.GetSum();
+ const auto& sumOfQuadruples = stats.m_SumOfQuadruples.GetSum();
+ const auto& sumOfPositivePixels = stats.m_SumOfPositivePixels.GetSum();
+
+ const RealType count = stats.m_Count;
+ const RealType countOfPositivePixels = stats.m_CountOfPositivePixels;
+
+ stats.m_Mean = sum / count;
+ const auto& mean = stats.m_Mean;
+
+ if (count > 1)
+ {
+ auto sumSquared = sum * sum;
+ stats.m_Variance = (sumOfSquares - sumSquared / count) / (count - 1.0);
+ }
+ else
+ {
+ stats.m_Variance = 0.0;
+ }
+
+ stats.m_Sigma = std::sqrt(stats.m_Variance);
+
+ const auto secondMoment = sumOfSquares / count;
+ const auto thirdMoment = sumOfCubes / count;
+ const auto fourthMoment = sumOfQuadruples / count;
+
+ stats.m_Skewness = (thirdMoment - 3 * secondMoment * mean + 2 * std::pow(mean, 3)) / std::pow(secondMoment - std::pow(mean, 2), 1.5);
+ stats.m_Kurtosis = (fourthMoment - 4 * thirdMoment * mean + 6 * secondMoment * std::pow(mean, 2) - 3 * std::pow(mean, 4)) / std::pow(secondMoment - std::pow(mean, 2), 2);
+ stats.m_MPP = sumOfPositivePixels / countOfPositivePixels;
+
+ if (m_ComputeHistograms)
+ {
+ mitk::HistogramStatisticsCalculator histogramStatisticsCalculator;
+ histogramStatisticsCalculator.SetHistogram(stats.m_Histogram);
+ histogramStatisticsCalculator.CalculateStatistics();
+
+ stats.m_Entropy = histogramStatisticsCalculator.GetEntropy();
+ stats.m_Uniformity = histogramStatisticsCalculator.GetUniformity();
+ stats.m_UPP = histogramStatisticsCalculator.GetUPP();
+ stats.m_Median = histogramStatisticsCalculator.GetMedian();
+ }
+ }
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::SetHistogramParameters(
+ const std::unordered_map<LabelPixelType, unsigned int>& sizes,
+ const std::unordered_map<LabelPixelType, RealType>& lowerBounds,
+ const std::unordered_map<LabelPixelType, RealType>& upperBounds) -> void
+{
+ bool modified = false;
+
+ if (m_HistogramSizes != sizes)
+ {
+ m_HistogramSizes = sizes;
+ modified = true;
+ }
+
+ if (m_HistogramLowerBounds != lowerBounds)
+ {
+ m_HistogramLowerBounds = lowerBounds;
+ modified = true;
+ }
+
+ if (m_HistogramUpperBounds != upperBounds)
+ {
+ m_HistogramUpperBounds = upperBounds;
+ modified = true;
+ }
+
+ m_ComputeHistograms = true;
+
+ if (modified)
+ this->Modified();
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::MergeMap(MapType& map1, MapType& map2) const -> void
+{
+ for (auto& elem2 : map2)
+ {
+ auto iter1 = map1.find(elem2.first);
+
+ if (map1.end() == iter1)
+ {
+ map1.emplace(elem2.first, std::move(elem2.second));
+ }
+ else
+ {
+ const auto label = iter1->first;
+ auto& stats1 = iter1->second;
+ auto& stats2 = elem2.second;
+
+ stats1.m_Min = std::min(stats1.m_Min, stats2.m_Min);
+ stats1.m_Max = std::max(stats1.m_Max, stats2.m_Max);
+
+ stats1.m_Sum += stats2.m_Sum;
+ stats1.m_SumOfSquares += stats2.m_SumOfSquares;
+ stats1.m_SumOfCubes += stats2.m_SumOfCubes;
+ stats1.m_SumOfQuadruples += stats2.m_SumOfQuadruples;
+ stats1.m_Count += stats2.m_Count;
+ stats1.m_SumOfPositivePixels += stats2.m_SumOfPositivePixels;
+ stats1.m_CountOfPositivePixels += stats2.m_CountOfPositivePixels;
+
+ for (unsigned int i = 0; i < (ImageDimension * 2); i += 2)
+ {
+ stats1.m_BoundingBox[i] = std::min(stats1.m_BoundingBox[i], stats2.m_BoundingBox[i]);
+ stats1.m_BoundingBox[i + 1] = std::max(stats1.m_BoundingBox[i + 1], stats2.m_BoundingBox[i + 1]);
+ }
+
+ if (m_ComputeHistograms)
+ {
+ typename HistogramType::IndexType index;
+ index.SetSize(1);
+
+ const auto histogramSize = m_HistogramSizes.at(label);
+
+ for (unsigned int bin = 0; bin < histogramSize; ++bin)
+ {
+ index[0] = bin;
+ stats1.m_Histogram->IncreaseFrequency(bin, stats2.m_Histogram->GetFrequency(bin));
+ }
+ }
+ }
+ }
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::HasLabel(LabelPixelType label) const -> bool
+{
+ return m_LabelStatistics.find(label) != m_LabelStatistics.end();
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetNumberOfObjects() const -> unsigned int
+{
+ return static_cast<unsigned int>(m_LabelStatistics.size());
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetNumberOfLabels() const -> unsigned int
+{
+ return this->GetNumberOfObjects();
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetValidLabelValues() const -> const ValidLabelValuesContainerType&
+{
+ return m_ValidLabelValues;
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetLabelStatistics(LabelPixelType label) const -> const LabelStatistics&
+{
+ MapConstIterator it = m_LabelStatistics.find(label);
+
+ if (m_LabelStatistics.end() != it)
+ return it->second;
+
+ mitkThrow() << "Label " << label << " does not exist";
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetLabelHistogramStatistics(LabelPixelType label) const -> const LabelStatistics&
+{
+ const auto& labelStatistics = this->GetLabelStatistics(label);
+
+ if (m_ComputeHistograms && labelStatistics.m_Histogram.IsNotNull())
+ return labelStatistics;
+
+ mitkThrow() << "Histogram was not computed for label " << label;
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetMinimum(LabelPixelType label) const -> PixelType
+{
+ const auto& labelStatistics = this->GetLabelStatistics(label);
+ return labelStatistics.m_Min;
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetMaximum(LabelPixelType label) const -> PixelType
+{
+ const auto& labelStatistics = this->GetLabelStatistics(label);
+ return labelStatistics.m_Max;
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetMean(LabelPixelType label) const -> RealType
+{
+ const auto& labelStatistics = this->GetLabelStatistics(label);
+ return labelStatistics.m_Mean;
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetSigma(LabelPixelType label) const -> RealType
+{
+ const auto& labelStatistics = this->GetLabelStatistics(label);
+ return labelStatistics.m_Sigma;
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetVariance(LabelPixelType label) const -> RealType
+{
+ const auto& labelStatistics = this->GetLabelStatistics(label);
+ return labelStatistics.m_Variance;
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetSkewness(LabelPixelType label) const ->RealType
+{
+ const auto& labelStatistics = this->GetLabelStatistics(label);
+ return labelStatistics.m_Skewness;
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetKurtosis(LabelPixelType label) const -> RealType
+{
+ const auto& labelStatistics = this->GetLabelStatistics(label);
+ return labelStatistics.m_Kurtosis;
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetMPP(LabelPixelType label) const -> RealType
+{
+ const auto& labelStatistics = this->GetLabelStatistics(label);
+ return labelStatistics.m_MPP;
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetCount(LabelPixelType label) const-> itk::SizeValueType
+{
+ const auto& labelStatistics = this->GetLabelStatistics(label);
+ return labelStatistics.m_Count;
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetHistogram(LabelPixelType label) const -> HistogramPointer
+{
+ const auto& labelStatistics = this->GetLabelHistogramStatistics(label);
+ return labelStatistics.m_Histogram;
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetEntropy(LabelPixelType label) const -> RealType
+{
+ const auto& labelStatistics = this->GetLabelHistogramStatistics(label);
+ return labelStatistics.m_Entropy;
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetUniformity(LabelPixelType label) const -> RealType
+{
+ const auto& labelStatistics = this->GetLabelHistogramStatistics(label);
+ return labelStatistics.m_Uniformity;
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetUPP(LabelPixelType label) const -> RealType
+{
+ const auto& labelStatistics = this->GetLabelHistogramStatistics(label);
+ return labelStatistics.m_UPP;
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetMedian(LabelPixelType label) const -> RealType
+{
+ const auto& labelStatistics = this->GetLabelHistogramStatistics(label);
+ return labelStatistics.m_Median;
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetSum(LabelPixelType label) const -> RealType
+{
+ const auto& labelStatistics = this->GetLabelStatistics(label);
+ return labelStatistics.m_Sum.GetSum();
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetSumOfSquares(LabelPixelType label) const -> RealType
+{
+ const auto& labelStatistics = this->GetLabelStatistics(label);
+ return labelStatistics.m_SumOfSquares.GetSum();
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetSumOfCubes(LabelPixelType label) const -> RealType
+{
+ const auto& labelStatistics = this->GetLabelStatistics(label);
+ return labelStatistics.m_SumOfCubes.GetSum();
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetSumOfQuadruples(LabelPixelType label) const -> RealType
+{
+ const auto& labelStatistics = this->GetLabelStatistics(label);
+ return labelStatistics.m_SumOfQuadruples.GetSum();
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetBoundingBox(LabelPixelType label) const -> BoundingBoxType
+{
+ const auto& labelStatistics = this->GetLabelStatistics(label);
+ return labelStatistics.m_BoundingBox;
+}
+
+template <typename TInputImage>
+auto mitk::LabelStatisticsImageFilter<TInputImage>::GetRegion(LabelPixelType label) const -> RegionType
+{
+ const auto& labelStatistics = this->GetLabelStatistics(label);
+
+ IndexType index;
+ SizeType size;
+
+ for (unsigned int i = 0; i < ImageDimension; ++i)
+ {
+ index[i] = labelStatistics.m_BoundingBox[2 * i];
+ size[i] = labelStatistics.m_BoundingBox[2 * i + 1] - labelStatistics.m_BoundingBox[2 * i] + 1;
+ }
+
+ RegionType region;
+ region.SetIndex(index);
+ region.SetSize(size);
+
+ return region;
+}
+
+template <typename TInputImage>
+void mitk::LabelStatisticsImageFilter<TInputImage>::PrintSelf(std::ostream& os, itk::Indent indent) const
+{
+ Superclass::PrintSelf(os, indent);
+
+ os << indent << "Number of labels: " << m_LabelStatistics.size() << std::endl;
+ os << indent << "Compute histograms: " << m_ComputeHistograms << std::endl;
+}
+
+#endif
diff --git a/Modules/ImageStatistics/mitkMinMaxImageFilterWithIndex.h b/Modules/ImageStatistics/mitkMinMaxImageFilterWithIndex.h
index 99ec49358c..b2053c718b 100644
--- a/Modules/ImageStatistics/mitkMinMaxImageFilterWithIndex.h
+++ b/Modules/ImageStatistics/mitkMinMaxImageFilterWithIndex.h
@@ -1,96 +1,101 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITK_MINMAXIMAGEFILTERWITHINDEX_H
#define MITK_MINMAXIMAGEFILTERWITHINDEX_H
#include <MitkImageStatisticsExports.h>
#include <itkImage.h>
#include <itkImageToImageFilter.h>
#include <itkImageRegionConstIteratorWithIndex.h>
namespace itk
{
template <typename TInputImage>
class MinMaxImageFilterWithIndex: public itk::ImageToImageFilter<TInputImage, TInputImage>
{
public:
/** Standard Self typedef */
typedef MinMaxImageFilterWithIndex Self;
typedef ImageToImageFilter< TInputImage, TInputImage > Superclass;
typedef SmartPointer< Self > Pointer;
typedef SmartPointer< const Self > ConstPointer;
/** Method for creation through the object factory. */
itkNewMacro(Self);
/** Runtime information support. */
itkTypeMacro(MinMaxImageFilterWithIndex, ImageToImageFilter);
typedef typename TInputImage::RegionType RegionType;
typedef typename TInputImage::SizeType SizeType;
typedef typename TInputImage::IndexType IndexType;
typedef typename TInputImage::PixelType PixelType;
typedef typename NumericTraits< PixelType >::RealType RealType;
RealType GetMin() const
{
return m_Min;
}
RealType GetMax() const
{
return m_Max;
}
IndexType GetMinIndex() const
{
return m_MinIndex;
}
IndexType GetMaxIndex() const
{
return m_MaxIndex;
}
protected:
+ MinMaxImageFilterWithIndex()
+ {
+ this->DynamicMultiThreadingOff();
+ }
+
void AllocateOutputs() override;
void ThreadedGenerateData(const RegionType &
outputRegionForThread,
ThreadIdType threadId) override;
void BeforeThreadedGenerateData() override;
void AfterThreadedGenerateData() override;
private:
std::vector<PixelType> m_ThreadMin;
std::vector<PixelType> m_ThreadMax;
std::vector<IndexType> m_ThreadMinIndex;
std::vector<IndexType> m_ThreadMaxIndex;
PixelType m_Min;
PixelType m_Max;
IndexType m_MinIndex;
IndexType m_MaxIndex;
};
}
#include "mitkMinMaxImageFilterWithIndex.hxx"
#endif
diff --git a/Modules/ImageStatistics/mitkMinMaxImageFilterWithIndex.hxx b/Modules/ImageStatistics/mitkMinMaxImageFilterWithIndex.hxx
index 77b4510f4f..7d50c1224b 100644
--- a/Modules/ImageStatistics/mitkMinMaxImageFilterWithIndex.hxx
+++ b/Modules/ImageStatistics/mitkMinMaxImageFilterWithIndex.hxx
@@ -1,109 +1,109 @@
#ifndef MITK_MinMaxImageFilterWithIndex_HXX
#define MITK_MinMaxImageFilterWithIndex_HXX
#include <mitkMinMaxImageFilterWithIndex.h>
#include <limits>
namespace itk
{
template< typename TInputImage >
void MinMaxImageFilterWithIndex< TInputImage >::AllocateOutputs()
{
// Pass the input through as the output
typename TInputImage::Pointer image =
const_cast< TInputImage * >( this->GetInput() );
this->GraftOutput(image);
// Nothing that needs to be allocated for the remaining outputs
}
template< typename TInputImage >
void MinMaxImageFilterWithIndex< TInputImage >::ThreadedGenerateData(const RegionType &
outputRegionForThread,
ThreadIdType threadId)
{
const SizeValueType size0 = outputRegionForThread.GetSize(0);
if( size0 == 0)
{
return;
}
PixelType value;
PixelType threadMin, threadMax;
IndexType threadMinIndex;
IndexType threadMaxIndex;
threadMinIndex.Fill(0);
threadMaxIndex.Fill(0);
threadMin = std::numeric_limits<PixelType>::max();
threadMax = std::numeric_limits<PixelType>::min();
ImageRegionConstIteratorWithIndex< TInputImage > it (this->GetInput(), outputRegionForThread);
// do the work
while ( !it.IsAtEnd() )
{
value = it.Get();
if (value < threadMin)
{
threadMin = value;
threadMinIndex = it.GetIndex();
}
if (value > threadMax)
{
threadMax = value;
threadMaxIndex = it.GetIndex();
}
++it;
}
m_ThreadMax[threadId] = threadMax;
m_ThreadMin[threadId] = threadMin;
m_ThreadMaxIndex[threadId] = threadMaxIndex;
m_ThreadMinIndex[threadId] = threadMinIndex;
}
template< typename TInputImage >
void MinMaxImageFilterWithIndex< TInputImage >::BeforeThreadedGenerateData()
{
- ThreadIdType numberOfThreads = this->GetNumberOfThreads();
+ ThreadIdType numberOfThreads = this->GetNumberOfWorkUnits();
m_ThreadMin.resize(numberOfThreads);
m_ThreadMax.resize(numberOfThreads);
m_ThreadMinIndex.resize(numberOfThreads);
m_ThreadMaxIndex.resize(numberOfThreads);
for (unsigned int i =0; i < numberOfThreads; i++)
{
m_ThreadMin[i] = std::numeric_limits<PixelType>::max();
m_ThreadMax[i] = std::numeric_limits<PixelType>::min();
}
m_Min = std::numeric_limits<PixelType>::max();
m_Max = std::numeric_limits<PixelType>::min();
}
template< typename TInputImage >
void MinMaxImageFilterWithIndex< TInputImage >::AfterThreadedGenerateData()
{
- ThreadIdType numberOfThreads = this->GetNumberOfThreads();
+ ThreadIdType numberOfThreads = this->GetNumberOfWorkUnits();
for (ThreadIdType i = 0; i < numberOfThreads; i++)
{
if (m_ThreadMin[i] < m_Min)
{
m_Min = m_ThreadMin[i];
m_MinIndex = m_ThreadMinIndex[i];
}
if (m_ThreadMax[i] > m_Max)
{
m_Max = m_ThreadMax[i];
m_MaxIndex = m_ThreadMaxIndex[i];
}
}
}
}
#endif
diff --git a/Modules/ImageStatistics/mitkMinMaxLabelmageFilterWithIndex.h b/Modules/ImageStatistics/mitkMinMaxLabelmageFilterWithIndex.h
index 2d53595178..ff38ff3d71 100644
--- a/Modules/ImageStatistics/mitkMinMaxLabelmageFilterWithIndex.h
+++ b/Modules/ImageStatistics/mitkMinMaxLabelmageFilterWithIndex.h
@@ -1,189 +1,194 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITK_MINMAXLABELIMAGEFILTERWITHINDEX_H
#define MITK_MINMAXLABELIMAGEFILTERWITHINDEX_H
#include <MitkImageStatisticsExports.h>
#include <itkImage.h>
#include <itkImageToImageFilter.h>
#include <itkImageRegionConstIteratorWithIndex.h>
-#include "itksys/hash_map.hxx"
+#include <unordered_map>
namespace itk
{
template <typename TInputImage, typename TLabelImage>
class MinMaxLabelImageFilterWithIndex: public itk::ImageToImageFilter<TInputImage, TInputImage>
{
public:
/** Standard Self typedef */
typedef MinMaxLabelImageFilterWithIndex Self;
typedef ImageToImageFilter< TInputImage, TInputImage > Superclass;
typedef SmartPointer< Self > Pointer;
typedef SmartPointer< const Self > ConstPointer;
/** Method for creation through the object factory. */
itkNewMacro(Self);
/** Runtime information support. */
itkTypeMacro(MinMaxLabelImageFilterWithIndex, ImageToImageFilter);
typedef typename TInputImage::RegionType RegionType;
typedef typename TInputImage::SizeType SizeType;
typedef typename TInputImage::IndexType IndexType;
typedef typename TInputImage::PixelType PixelType;
typedef typename NumericTraits< PixelType >::RealType RealType;
typedef typename TLabelImage::RegionType LabelRegionType;
typedef typename TLabelImage::SizeType LabelSizeType;
typedef typename TLabelImage::IndexType LabelIndexType;
typedef typename TLabelImage::PixelType LabelPixelType;
/**
* @brief The LabelExtrema class is just a container for global min/max values and their indices as well as all min and max values (+indices) of the mask labels
*/
class LabelExtrema
{
public:
PixelType m_Min, m_Max;
IndexType m_MinIndex, m_MaxIndex;
LabelExtrema():
m_Min(std::numeric_limits<PixelType>::max()),
m_Max(std::numeric_limits<PixelType>::min())
{}
};
- typedef typename itksys::hash_map<LabelPixelType, LabelExtrema> ExtremaMapType;
- typedef typename ExtremaMapType::iterator ExtremaMapTypeIterator;
- typedef typename ExtremaMapType::const_iterator ExtremaMapTypeConstIterator;
- typedef typename ExtremaMapType::value_type MapValueType;
+ typedef typename std::unordered_map<LabelPixelType, LabelExtrema> ExtremaMapType;
+ typedef typename ExtremaMapType::iterator ExtremaMapTypeIterator;
+ typedef typename ExtremaMapType::const_iterator ExtremaMapTypeConstIterator;
+ typedef typename ExtremaMapType::value_type MapValueType;
PixelType GetMin(LabelPixelType label) const
{
ExtremaMapTypeConstIterator it = m_LabelExtrema.find(label);
if (it == m_LabelExtrema.end())
{
MITK_ERROR << "invalid label";
}
return (*it).second.m_Min;
}
PixelType GetMax(LabelPixelType label) const
{
ExtremaMapTypeConstIterator it = m_LabelExtrema.find(label);
if (it == m_LabelExtrema.end())
{
MITK_ERROR << "invalid label";
}
return (*it).second.m_Max;
}
/**
* @brief Returns a std::vector containing all labels for which min and max values (and indices) have been computed
*/
std::vector<LabelPixelType> GetRelevantLabels() const
{
std::vector<LabelPixelType> labels;
for (auto&& it:m_LabelExtrema)
{
labels.push_back(it.first);
}
return labels;
}
IndexType GetMinIndex(LabelPixelType label) const
{
ExtremaMapTypeConstIterator it = m_LabelExtrema.find(label);
if (it == m_LabelExtrema.end())
{
MITK_ERROR << "invalid label";
}
return (*it).second.m_MinIndex;
}
IndexType GetMaxIndex(LabelPixelType label) const
{
ExtremaMapTypeConstIterator it = m_LabelExtrema.find(label);
if (it == m_LabelExtrema.end())
{
MITK_ERROR << "invalid label";
}
return (*it).second.m_MaxIndex;
}
PixelType GetGlobalMin() const
{
return m_GlobalMin;
}
PixelType GetGlobalMax() const
{
return m_GlobalMax;
}
IndexType GetGlobalMinIndex() const
{
return m_GlobalMinIndex;
}
IndexType GetGlobalMaxIndex() const
{
return m_GlobalMaxIndex;
}
/** Set the label image */
void SetLabelInput(const TLabelImage *input)
{
// Process object is not const-correct so the const casting is required.
this->SetNthInput( 1, const_cast< TLabelImage * >( input ) );
}
/** Get the label image */
const TLabelImage * GetLabelInput() const
{
return itkDynamicCastInDebugMode< TLabelImage * >( const_cast< DataObject * >( this->ProcessObject::GetInput(1) ) );
}
protected:
+ MinMaxLabelImageFilterWithIndex()
+ {
+ this->DynamicMultiThreadingOff();
+ }
+
void AllocateOutputs() override;
void ThreadedGenerateData(const RegionType &
outputRegionForThread,
ThreadIdType threadId) override;
void BeforeThreadedGenerateData() override;
void AfterThreadedGenerateData() override;
private:
std::vector<ExtremaMapType> m_ThreadExtrema;
ExtremaMapType m_LabelExtrema;
PixelType m_GlobalMin;
PixelType m_GlobalMax;
IndexType m_GlobalMinIndex, m_GlobalMaxIndex;
};
}
#include "mitkMinMaxLabelmageFilterWithIndex.hxx"
#endif
diff --git a/Modules/ImageStatistics/mitkMinMaxLabelmageFilterWithIndex.hxx b/Modules/ImageStatistics/mitkMinMaxLabelmageFilterWithIndex.hxx
index c1a0af7ae2..89dc9d1300 100644
--- a/Modules/ImageStatistics/mitkMinMaxLabelmageFilterWithIndex.hxx
+++ b/Modules/ImageStatistics/mitkMinMaxLabelmageFilterWithIndex.hxx
@@ -1,130 +1,130 @@
#ifndef MITK_MinMaxLabelImageFilterWithIndex_HXX
#define MITK_MinMaxLabelImageFilterWithIndex_HXX
#include <mitkMinMaxLabelmageFilterWithIndex.h>
#include <limits>
namespace itk
{
template< typename TInputImage, typename TLabelImage >
void MinMaxLabelImageFilterWithIndex< TInputImage, TLabelImage >::AllocateOutputs()
{
// Pass the input through as the output
typename TInputImage::Pointer image =
const_cast< TInputImage * >( this->GetInput() );
this->GraftOutput(image);
// Nothing that needs to be allocated for the remaining outputs
}
template< typename TInputImage, typename TLabelImage >
void MinMaxLabelImageFilterWithIndex< TInputImage, TLabelImage >::ThreadedGenerateData(const RegionType &
outputRegionForThread,
ThreadIdType threadId)
{
const SizeValueType size0 = outputRegionForThread.GetSize(0);
if( size0 == 0)
{
return;
}
PixelType value;
LabelPixelType label;
ExtremaMapType threadExtrema;
ExtremaMapTypeIterator threadExtremaIt;
ImageRegionConstIteratorWithIndex< TInputImage > it (this->GetInput(), outputRegionForThread);
ImageRegionConstIteratorWithIndex< TLabelImage > labelit (this->GetLabelInput(), outputRegionForThread);
// do the work
while ( !it.IsAtEnd() )
{
value = it.Get();
label = labelit.Get();
threadExtremaIt = threadExtrema.find(label);
// if label does not exist yet, create a new entry in the map.
if (threadExtremaIt == threadExtrema.end())
{
threadExtremaIt = threadExtrema.insert( MapValueType(label, LabelExtrema()) ).first;
}
if (value < (*threadExtremaIt).second.m_Min)
{
(*threadExtremaIt).second.m_Min = value;
(*threadExtremaIt).second.m_MinIndex = it.GetIndex();
}
if (value > (*threadExtremaIt).second.m_Max)
{
(*threadExtremaIt).second.m_Max = value;
(*threadExtremaIt).second.m_MaxIndex = it.GetIndex();
}
++it;
++labelit;
}
m_ThreadExtrema[threadId] = threadExtrema;
}
template< typename TInputImage, typename TLabelImage >
void MinMaxLabelImageFilterWithIndex< TInputImage, TLabelImage >::BeforeThreadedGenerateData()
{
- ThreadIdType numberOfThreads = this->GetNumberOfThreads();
+ ThreadIdType numberOfThreads = this->GetNumberOfWorkUnits();
m_ThreadExtrema.resize(numberOfThreads);
for (unsigned int i =0; i < numberOfThreads; i++)
{
m_ThreadExtrema[i] = ExtremaMapType();
}
}
template< typename TInputImage, typename TLabelImage >
void MinMaxLabelImageFilterWithIndex< TInputImage, TLabelImage >::AfterThreadedGenerateData()
{
- ThreadIdType numberOfThreads = this->GetNumberOfThreads();
+ ThreadIdType numberOfThreads = this->GetNumberOfWorkUnits();
m_GlobalMin = std::numeric_limits<PixelType>::max();
m_GlobalMax = std::numeric_limits<PixelType>::min();
ExtremaMapTypeIterator it;
for (ThreadIdType i = 0; i < numberOfThreads; i++)
{
for (auto&& it2 : m_ThreadExtrema[i])
{
it = m_LabelExtrema.find(it2.first);
if (it == m_LabelExtrema.end())
{
it = m_LabelExtrema.insert( MapValueType(it2.first, LabelExtrema()) ).first;
}
if (it2.second.m_Min < (*it).second.m_Min)
{
(*it).second.m_Min = it2.second.m_Min;
(*it).second.m_MinIndex = it2.second.m_MinIndex;
if (it2.second.m_Min < m_GlobalMin)
{
m_GlobalMin = it2.second.m_Min;
m_GlobalMinIndex = it2.second.m_MinIndex;
}
}
if (it2.second.m_Max > (*it).second.m_Max)
{
(*it).second.m_Max = it2.second.m_Max;
(*it).second.m_MaxIndex = it2.second.m_MaxIndex;
if (it2.second.m_Max > m_GlobalMax)
{
m_GlobalMax = it2.second.m_Max;
m_GlobalMaxIndex = it2.second.m_MaxIndex;
}
}
}
}
}
}
#endif
diff --git a/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp b/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp
index 2663d77380..6acffbfdf4 100644
--- a/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp
+++ b/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp
@@ -1,512 +1,512 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <mitkPlanarFigureMaskGenerator.h>
#include <mitkBaseGeometry.h>
#include <mitkITKImageImport.h>
#include "mitkImageAccessByItk.h"
#include <mitkExtractImageFilter.h>
#include <mitkConvert2Dto3DImageFilter.h>
#include <mitkImageTimeSelector.h>
#include <mitkIOUtil.h>
#include <itkCastImageFilter.h>
#include <itkVTKImageExport.h>
#include <itkVTKImageImport.h>
#include <itkImageDuplicator.h>
-#include <itkExceptionObject.h>
+#include <itkMacro.h>
#include <itkLineIterator.h>
#include <vtkPoints.h>
#include <vtkImageStencil.h>
#include <vtkImageImport.h>
#include <vtkImageExport.h>
#include <vtkLassoStencilSource.h>
#include <vtkSmartPointer.h>
namespace mitk
{
void PlanarFigureMaskGenerator::SetPlanarFigure(mitk::PlanarFigure::Pointer planarFigure)
{
if ( planarFigure.IsNull() )
{
throw std::runtime_error( "Error: planar figure empty!" );
}
const PlaneGeometry *planarFigurePlaneGeometry = planarFigure->GetPlaneGeometry();
if ( planarFigurePlaneGeometry == nullptr )
{
throw std::runtime_error( "Planar-Figure not yet initialized!" );
}
const auto *planarFigureGeometry =
dynamic_cast< const PlaneGeometry * >( planarFigurePlaneGeometry );
if ( planarFigureGeometry == nullptr )
{
throw std::runtime_error( "Non-planar planar figures not supported!" );
}
if (planarFigure != m_PlanarFigure)
{
this->Modified();
m_PlanarFigure = planarFigure;
}
}
mitk::Image::ConstPointer PlanarFigureMaskGenerator::GetReferenceImage()
{
if (IsUpdateRequired())
{
this->CalculateMask();
}
return m_ReferenceImage;
}
template < typename TPixel, unsigned int VImageDimension >
void PlanarFigureMaskGenerator::InternalCalculateMaskFromPlanarFigure(
const itk::Image< TPixel, VImageDimension > *image, unsigned int axis )
{
typedef itk::Image< unsigned short, 2 > MaskImage2DType;
typename MaskImage2DType::Pointer maskImage = MaskImage2DType::New();
maskImage->SetOrigin(image->GetOrigin());
maskImage->SetSpacing(image->GetSpacing());
maskImage->SetLargestPossibleRegion(image->GetLargestPossibleRegion());
maskImage->SetBufferedRegion(image->GetBufferedRegion());
maskImage->SetDirection(image->GetDirection());
maskImage->SetNumberOfComponentsPerPixel(image->GetNumberOfComponentsPerPixel());
maskImage->Allocate();
maskImage->FillBuffer(1);
// all PolylinePoints of the PlanarFigure are stored in a vtkPoints object.
// These points are used by the vtkLassoStencilSource to create
// a vtkImageStencil.
const mitk::PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry();
const typename PlanarFigure::PolyLineType planarFigurePolyline = m_PlanarFigure->GetPolyLine( 0 );
const mitk::BaseGeometry *imageGeometry3D = m_inputImage->GetGeometry( 0 );
// If there is a second poly line in a closed planar figure, treat it as a hole.
PlanarFigure::PolyLineType planarFigureHolePolyline;
if (m_PlanarFigure->GetPolyLinesSize() == 2)
planarFigureHolePolyline = m_PlanarFigure->GetPolyLine(1);
// Determine x- and y-dimensions depending on principal axis
// TODO use plane geometry normal to determine that automatically, then check whether the PF is aligned with one of the three principal axis
int i0, i1;
switch ( axis )
{
case 0:
i0 = 1;
i1 = 2;
break;
case 1:
i0 = 0;
i1 = 2;
break;
case 2:
default:
i0 = 0;
i1 = 1;
break;
}
// store the polyline contour as vtkPoints object
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
for (const auto& point : planarFigurePolyline)
{
Point3D point3D;
// Convert 2D point back to the local index coordinates of the selected image
planarFigurePlaneGeometry->Map(point, point3D);
imageGeometry3D->WorldToIndex(point3D, point3D);
points->InsertNextPoint(point3D[i0], point3D[i1], 0);
}
vtkSmartPointer<vtkPoints> holePoints;
if (!planarFigureHolePolyline.empty())
{
holePoints = vtkSmartPointer<vtkPoints>::New();
Point3D point3D;
for (const auto& point : planarFigureHolePolyline)
{
planarFigurePlaneGeometry->Map(point, point3D);
imageGeometry3D->WorldToIndex(point3D, point3D);
holePoints->InsertNextPoint(point3D[i0], point3D[i1], 0);
}
}
// mark a malformed 2D planar figure ( i.e. area = 0 ) as out of bounds
// this can happen when all control points of a rectangle lie on the same line = two of the three extents are zero
double bounds[6] = {0};
points->GetBounds(bounds);
bool extent_x = (fabs(bounds[0] - bounds[1])) < mitk::eps;
bool extent_y = (fabs(bounds[2] - bounds[3])) < mitk::eps;
bool extent_z = (fabs(bounds[4] - bounds[5])) < mitk::eps;
// throw an exception if a closed planar figure is deformed, i.e. has only one non-zero extent
if (m_PlanarFigure->IsClosed() && ((extent_x && extent_y) || (extent_x && extent_z) || (extent_y && extent_z)))
{
mitkThrow() << "Figure has a zero area and cannot be used for masking.";
}
// create a vtkLassoStencilSource and set the points of the Polygon
vtkSmartPointer<vtkLassoStencilSource> lassoStencil = vtkSmartPointer<vtkLassoStencilSource>::New();
lassoStencil->SetShapeToPolygon();
lassoStencil->SetPoints(points);
vtkSmartPointer<vtkLassoStencilSource> holeLassoStencil = nullptr;
if (holePoints.GetPointer() != nullptr)
{
holeLassoStencil = vtkSmartPointer<vtkLassoStencilSource>::New();
holeLassoStencil->SetShapeToPolygon();
holeLassoStencil->SetPoints(holePoints);
}
// Export from ITK to VTK (to use a VTK filter)
typedef itk::VTKImageImport< MaskImage2DType > ImageImportType;
typedef itk::VTKImageExport< MaskImage2DType > ImageExportType;
typename ImageExportType::Pointer itkExporter = ImageExportType::New();
itkExporter->SetInput( maskImage );
// itkExporter->SetInput( castFilter->GetOutput() );
vtkSmartPointer<vtkImageImport> vtkImporter = vtkSmartPointer<vtkImageImport>::New();
this->ConnectPipelines( itkExporter, vtkImporter );
// Apply the generated image stencil to the input image
vtkSmartPointer<vtkImageStencil> imageStencilFilter = vtkSmartPointer<vtkImageStencil>::New();
imageStencilFilter->SetInputConnection( vtkImporter->GetOutputPort() );
imageStencilFilter->SetStencilConnection(lassoStencil->GetOutputPort());
imageStencilFilter->ReverseStencilOff();
imageStencilFilter->SetBackgroundValue( 0 );
imageStencilFilter->Update();
vtkSmartPointer<vtkImageStencil> holeStencilFilter = nullptr;
if (holeLassoStencil.GetPointer() != nullptr)
{
holeStencilFilter = vtkSmartPointer<vtkImageStencil>::New();
holeStencilFilter->SetInputConnection(imageStencilFilter->GetOutputPort());
holeStencilFilter->SetStencilConnection(holeLassoStencil->GetOutputPort());
holeStencilFilter->ReverseStencilOn();
holeStencilFilter->SetBackgroundValue(0);
holeStencilFilter->Update();
}
// Export from VTK back to ITK
vtkSmartPointer<vtkImageExport> vtkExporter = vtkSmartPointer<vtkImageExport>::New();
vtkExporter->SetInputConnection( holeStencilFilter.GetPointer() == nullptr
? imageStencilFilter->GetOutputPort()
: holeStencilFilter->GetOutputPort());
vtkExporter->Update();
typename ImageImportType::Pointer itkImporter = ImageImportType::New();
this->ConnectPipelines( vtkExporter, itkImporter );
itkImporter->Update();
typedef itk::ImageDuplicator< ImageImportType::OutputImageType > DuplicatorType;
DuplicatorType::Pointer duplicator = DuplicatorType::New();
duplicator->SetInputImage( itkImporter->GetOutput() );
duplicator->Update();
// Store mask
m_InternalITKImageMask2D = duplicator->GetOutput();
}
template < typename TPixel, unsigned int VImageDimension >
void PlanarFigureMaskGenerator::InternalCalculateMaskFromOpenPlanarFigure(
const itk::Image< TPixel, VImageDimension > *image, unsigned int axis )
{
typedef itk::Image< unsigned short, 2 > MaskImage2DType;
typedef itk::LineIterator< MaskImage2DType > LineIteratorType;
typedef MaskImage2DType::IndexType IndexType2D;
typedef std::vector< IndexType2D > IndexVecType;
typename MaskImage2DType::Pointer maskImage = MaskImage2DType::New();
maskImage->SetOrigin(image->GetOrigin());
maskImage->SetSpacing(image->GetSpacing());
maskImage->SetLargestPossibleRegion(image->GetLargestPossibleRegion());
maskImage->SetBufferedRegion(image->GetBufferedRegion());
maskImage->SetDirection(image->GetDirection());
maskImage->SetNumberOfComponentsPerPixel(image->GetNumberOfComponentsPerPixel());
maskImage->Allocate();
maskImage->FillBuffer(0);
// all PolylinePoints of the PlanarFigure are stored in a vtkPoints object.
const mitk::PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry();
const typename PlanarFigure::PolyLineType planarFigurePolyline = m_PlanarFigure->GetPolyLine( 0 );
const mitk::BaseGeometry *imageGeometry3D = m_inputImage->GetGeometry( 0 );
// Determine x- and y-dimensions depending on principal axis
// TODO use plane geometry normal to determine that automatically, then check whether the PF is aligned with one of the three principal axis
int i0, i1;
switch ( axis )
{
case 0:
i0 = 1;
i1 = 2;
break;
case 1:
i0 = 0;
i1 = 2;
break;
case 2:
default:
i0 = 0;
i1 = 1;
break;
}
int numPolyLines = m_PlanarFigure->GetPolyLinesSize();
for ( int lineId = 0; lineId < numPolyLines; ++lineId )
{
// store the polyline contour as vtkPoints object
IndexVecType pointIndices;
for(const auto& point : planarFigurePolyline)
{
Point3D point3D;
planarFigurePlaneGeometry->Map(point, point3D);
imageGeometry3D->WorldToIndex(point3D, point3D);
IndexType2D index2D;
index2D[0] = point3D[i0];
index2D[1] = point3D[i1];
pointIndices.push_back( index2D );
}
size_t numLineSegments = pointIndices.size() - 1;
for (size_t i = 0; i < numLineSegments; ++i)
{
LineIteratorType lineIt(maskImage, pointIndices[i], pointIndices[i+1]);
while (!lineIt.IsAtEnd())
{
lineIt.Set(1);
++lineIt;
}
}
}
// Store mask
m_InternalITKImageMask2D = maskImage;
}
bool PlanarFigureMaskGenerator::CheckPlanarFigureIsNotTilted(const PlaneGeometry* planarGeometry, const BaseGeometry *geometry)
{
if (!planarGeometry) return false;
if (!geometry) return false;
unsigned int axis;
return GetPrincipalAxis(geometry,planarGeometry->GetNormal(), axis);
}
bool PlanarFigureMaskGenerator::GetPrincipalAxis(
const BaseGeometry *geometry, Vector3D vector,
unsigned int &axis )
{
vector.Normalize();
for ( unsigned int i = 0; i < 3; ++i )
{
Vector3D axisVector = geometry->GetAxisVector( i );
axisVector.Normalize();
//normal mitk::eps is to pedantic for this check. See e.g. T27122
//therefore choose a larger epsilon. The value was set a) as small as
//possible but b) still allowing to datasets like in (T27122) to pass
//when floating rounding errors sum up.
const double epsilon = 5e-5;
if ( fabs( fabs( axisVector * vector ) - 1.0) < epsilon)
{
axis = i;
return true;
}
}
return false;
}
void PlanarFigureMaskGenerator::CalculateMask()
{
if (m_inputImage.IsNull())
{
MITK_ERROR << "Image is not set.";
}
if (m_PlanarFigure.IsNull())
{
MITK_ERROR << "PlanarFigure is not set.";
}
if (m_TimeStep != 0)
{
MITK_WARN << "Multiple TimeSteps are not supported in PlanarFigureMaskGenerator (yet).";
}
const BaseGeometry *imageGeometry = m_inputImage->GetGeometry();
if ( imageGeometry == nullptr )
{
throw std::runtime_error( "Image geometry invalid!" );
}
if (m_inputImage->GetTimeSteps() > 0)
{
mitk::ImageTimeSelector::Pointer imgTimeSel = mitk::ImageTimeSelector::New();
imgTimeSel->SetInput(m_inputImage);
imgTimeSel->SetTimeNr(m_TimeStep);
imgTimeSel->UpdateLargestPossibleRegion();
m_InternalTimeSliceImage = imgTimeSel->GetOutput();
}
else
{
m_InternalTimeSliceImage = m_inputImage;
}
m_InternalITKImageMask2D = nullptr;
const PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry();
const auto *planarFigureGeometry = dynamic_cast< const PlaneGeometry * >( planarFigurePlaneGeometry );
//const BaseGeometry *imageGeometry = m_inputImage->GetGeometry();
// Find principal direction of PlanarFigure in input image
unsigned int axis;
if ( !this->GetPrincipalAxis( imageGeometry,
planarFigureGeometry->GetNormal(), axis ) )
{
throw std::runtime_error( "Non-aligned planar figures not supported!" );
}
m_PlanarFigureAxis = axis;
// Find slice number corresponding to PlanarFigure in input image
itk::Image< unsigned short, 3 >::IndexType index;
imageGeometry->WorldToIndex( planarFigureGeometry->GetOrigin(), index );
unsigned int slice = index[axis];
m_PlanarFigureSlice = slice;
// extract image slice which corresponds to the planarFigure and store it in m_InternalImageSlice
mitk::Image::ConstPointer inputImageSlice = extract2DImageSlice(axis, slice);
//mitk::IOUtil::Save(inputImageSlice, "/home/fabian/inputSliceImage.nrrd");
// Compute mask from PlanarFigure
// rastering for open planar figure:
if ( !m_PlanarFigure->IsClosed() )
{
AccessFixedDimensionByItk_1(inputImageSlice,
InternalCalculateMaskFromOpenPlanarFigure,
2, axis)
}
else//for closed planar figure
{
AccessFixedDimensionByItk_1(inputImageSlice,
InternalCalculateMaskFromPlanarFigure,
2, axis)
}
//convert itk mask to mitk::Image::Pointer and return it
mitk::Image::Pointer planarFigureMaskImage;
planarFigureMaskImage = mitk::GrabItkImageMemory(m_InternalITKImageMask2D);
//mitk::IOUtil::Save(planarFigureMaskImage, "/home/fabian/planarFigureMaskImage.nrrd");
//Convert2Dto3DImageFilter::Pointer sliceTo3DImageConverter = Convert2Dto3DImageFilter::New();
//sliceTo3DImageConverter->SetInput(planarFigureMaskImage);
//sliceTo3DImageConverter->Update();
//mitk::IOUtil::Save(sliceTo3DImageConverter->GetOutput(), "/home/fabian/3DsliceImage.nrrd");
m_ReferenceImage = inputImageSlice;
//mitk::IOUtil::Save(m_ReferenceImage, "/home/fabian/referenceImage.nrrd");
m_InternalMask = planarFigureMaskImage;
}
void PlanarFigureMaskGenerator::SetTimeStep(unsigned int timeStep)
{
if (timeStep != m_TimeStep)
{
m_TimeStep = timeStep;
}
}
mitk::Image::Pointer PlanarFigureMaskGenerator::GetMask()
{
if (IsUpdateRequired())
{
this->CalculateMask();
this->Modified();
}
m_InternalMaskUpdateTime = this->GetMTime();
return m_InternalMask;
}
mitk::Image::ConstPointer PlanarFigureMaskGenerator::extract2DImageSlice(unsigned int axis, unsigned int slice)
{
// Extract slice with given position and direction from image
unsigned int dimension = m_InternalTimeSliceImage->GetDimension();
if (dimension == 3)
{
ExtractImageFilter::Pointer imageExtractor = ExtractImageFilter::New();
imageExtractor->SetInput( m_InternalTimeSliceImage );
imageExtractor->SetSliceDimension( axis );
imageExtractor->SetSliceIndex( slice );
imageExtractor->Update();
return imageExtractor->GetOutput();
}
else if(dimension == 2)
{
return m_InternalTimeSliceImage;
}
else
{
MITK_ERROR << "Unsupported image dimension. Dimension is: " << dimension << ". Only 2D and 3D images are supported.";
return nullptr;
}
}
bool PlanarFigureMaskGenerator::IsUpdateRequired() const
{
unsigned long thisClassTimeStamp = this->GetMTime();
unsigned long internalMaskTimeStamp = m_InternalMask->GetMTime();
unsigned long planarFigureTimeStamp = m_PlanarFigure->GetMTime();
unsigned long inputImageTimeStamp = m_inputImage->GetMTime();
if (thisClassTimeStamp > m_InternalMaskUpdateTime) // inputs have changed
{
return true;
}
if (m_InternalMaskUpdateTime < planarFigureTimeStamp || m_InternalMaskUpdateTime < inputImageTimeStamp) // mask image has changed outside of this class
{
return true;
}
if (internalMaskTimeStamp > m_InternalMaskUpdateTime) // internal mask has been changed outside of this class
{
return true;
}
return false;
}
}
diff --git a/Modules/ImageStatistics/mitkStatisticsImageFilter.h b/Modules/ImageStatistics/mitkStatisticsImageFilter.h
new file mode 100644
index 0000000000..54abfc4cf3
--- /dev/null
+++ b/Modules/ImageStatistics/mitkStatisticsImageFilter.h
@@ -0,0 +1,143 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+// This file is based on ITK's itkStatisticsImageFilter.h
+
+#ifndef mitkStatisticsImageFilter
+#define mitkStatisticsImageFilter
+
+#include <mitkCommon.h>
+
+#include <itkArray.h>
+#include <itkCompensatedSummation.h>
+#include <itkHistogram.h>
+#include <itkImageSink.h>
+#include <itkNumericTraits.h>
+#include <itkSimpleDataObjectDecorator.h>
+
+#include <mutex>
+
+namespace mitk
+{
+ template <typename TInputImage>
+ class StatisticsImageFilter : public itk::ImageSink<TInputImage>
+ {
+ public:
+ using Self = StatisticsImageFilter;
+ using Superclass = itk::ImageSink<TInputImage>;
+ using Pointer = itk::SmartPointer<Self>;
+ using ConstPointer = itk::SmartPointer<const Self>;
+
+ itkFactorylessNewMacro(Self);
+
+ itkTypeMacro(StatisticsImageFilter, itk::ImageSink);
+
+ using RegionType = typename TInputImage::RegionType;
+ using PixelType = typename TInputImage::PixelType;
+
+ using RealType = typename itk::NumericTraits<PixelType>::RealType;
+
+ using HistogramType = typename itk::Statistics::Histogram<RealType>;
+ using HistogramPointer = itk::SmartPointer<HistogramType>;
+
+ using DataObjectPointer = typename itk::DataObject::Pointer;
+
+ template <typename T>
+ using SimpleDataObjectDecorator = itk::SimpleDataObjectDecorator<T>;
+
+ using RealObjectType = SimpleDataObjectDecorator<RealType>;
+ using PixelObjectType = SimpleDataObjectDecorator<PixelType>;
+ using ProcessObject = itk::ProcessObject;
+
+ itkGetDecoratedOutputMacro(Minimum, PixelType);
+ itkGetDecoratedOutputMacro(Maximum, PixelType);
+ itkGetDecoratedOutputMacro(Mean, RealType);
+ itkGetDecoratedOutputMacro(Sigma, RealType);
+ itkGetDecoratedOutputMacro(Variance, RealType);
+ itkGetDecoratedOutputMacro(Sum, RealType);
+ itkGetDecoratedOutputMacro(SumOfSquares, RealType);
+ itkGetDecoratedOutputMacro(SumOfCubes, RealType);
+ itkGetDecoratedOutputMacro(SumOfQuadruples, RealType);
+ itkGetDecoratedOutputMacro(Skewness, RealType);
+ itkGetDecoratedOutputMacro(Kurtosis, RealType);
+ itkGetDecoratedOutputMacro(MPP, RealType);
+ itkGetDecoratedOutputMacro(Histogram, HistogramPointer);
+ itkGetDecoratedOutputMacro(Entropy, RealType);
+ itkGetDecoratedOutputMacro(Uniformity, RealType);
+ itkGetDecoratedOutputMacro(UPP, RealType);
+ itkGetDecoratedOutputMacro(Median, RealType);
+
+ void SetHistogramParameters(unsigned int size, RealType lowerBound, RealType upperBound);
+
+ using DataObjectIdentifierType = itk::ProcessObject::DataObjectIdentifierType;
+ using Superclass::MakeOutput;
+
+ /** Make a DataObject of the correct type to be used as the specified output. */
+ DataObjectPointer MakeOutput(const DataObjectIdentifierType& name) override;
+
+ protected:
+ StatisticsImageFilter();
+ ~StatisticsImageFilter();
+
+ itkSetDecoratedOutputMacro(Minimum, PixelType);
+ itkSetDecoratedOutputMacro(Maximum, PixelType);
+ itkSetDecoratedOutputMacro(Mean, RealType);
+ itkSetDecoratedOutputMacro(Sigma, RealType);
+ itkSetDecoratedOutputMacro(Variance, RealType);
+ itkSetDecoratedOutputMacro(Sum, RealType);
+ itkSetDecoratedOutputMacro(SumOfSquares, RealType);
+ itkSetDecoratedOutputMacro(SumOfCubes, RealType);
+ itkSetDecoratedOutputMacro(SumOfQuadruples, RealType);
+ itkSetDecoratedOutputMacro(Skewness, RealType);
+ itkSetDecoratedOutputMacro(Kurtosis, RealType);
+ itkSetDecoratedOutputMacro(MPP, RealType);
+ itkSetDecoratedOutputMacro(Histogram, HistogramPointer);
+ itkSetDecoratedOutputMacro(Entropy, RealType);
+ itkSetDecoratedOutputMacro(Uniformity, RealType);
+ itkSetDecoratedOutputMacro(UPP, RealType);
+ itkSetDecoratedOutputMacro(Median, RealType);
+
+ void BeforeStreamedGenerateData() override;
+ void ThreadedStreamedGenerateData(const RegionType&) override;
+ void AfterStreamedGenerateData() override;
+
+ void PrintSelf(std::ostream& os, itk::Indent indent) const override;
+
+ private:
+ HistogramPointer CreateInitializedHistogram() const;
+
+ bool m_ComputeHistogram;
+ unsigned int m_HistogramSize;
+ RealType m_HistogramLowerBound;
+ RealType m_HistogramUpperBound;
+ HistogramPointer m_Histogram;
+
+ itk::CompensatedSummation<RealType> m_Sum;
+ itk::CompensatedSummation<RealType> m_SumOfPositivePixels;
+ itk::CompensatedSummation<RealType> m_SumOfSquares;
+ itk::CompensatedSummation<RealType> m_SumOfCubes;
+ itk::CompensatedSummation<RealType> m_SumOfQuadruples;
+
+ itk::SizeValueType m_Count;
+ itk::SizeValueType m_CountOfPositivePixels;
+ PixelType m_Min;
+ PixelType m_Max;
+
+ std::mutex m_Mutex;
+ };
+}
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include <mitkStatisticsImageFilter.hxx>
+#endif
+
+#endif
diff --git a/Modules/ImageStatistics/mitkStatisticsImageFilter.hxx b/Modules/ImageStatistics/mitkStatisticsImageFilter.hxx
new file mode 100644
index 0000000000..8c44f722dc
--- /dev/null
+++ b/Modules/ImageStatistics/mitkStatisticsImageFilter.hxx
@@ -0,0 +1,325 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#ifndef mitkStatisticsImageFilter_hxx
+#define mitkStatisticsImageFilter_hxx
+
+#include <mitkStatisticsImageFilter.h>
+#include <mitkHistogramStatisticsCalculator.h>
+#include <itkImageScanlineConstIterator.h>
+
+template <typename TInputImage>
+mitk::StatisticsImageFilter<TInputImage>::StatisticsImageFilter()
+ : m_ComputeHistogram(false),
+ m_HistogramSize(0),
+ m_HistogramLowerBound(itk::NumericTraits<RealType>::NonpositiveMin()),
+ m_HistogramUpperBound(itk::NumericTraits<RealType>::max()),
+ m_Sum(1),
+ m_SumOfPositivePixels(1),
+ m_SumOfSquares(1),
+ m_SumOfCubes(1),
+ m_SumOfQuadruples(1),
+ m_Count(1),
+ m_CountOfPositivePixels(1),
+ m_Min(1),
+ m_Max(1)
+{
+ this->SetNumberOfRequiredInputs(1);
+
+ this->SetMinimum(itk::NumericTraits<PixelType>::max());
+ this->SetMaximum(itk::NumericTraits<PixelType>::NonpositiveMin());
+ this->SetMean(itk::NumericTraits<RealType>::max());
+ this->SetSigma(itk::NumericTraits<RealType>::max());
+ this->SetVariance(itk::NumericTraits<RealType>::max());
+ this->SetSum(0);
+ this->SetSumOfSquares(0);
+ this->SetSumOfCubes(0);
+ this->SetSumOfQuadruples(0);
+ this->SetSkewness(0);
+ this->SetKurtosis(0);
+ this->SetMPP(0);
+ this->SetEntropy(-1.0);
+ this->SetUniformity(0);
+ this->SetUPP(0);
+ this->SetMedian(0);
+}
+
+template <typename TInputImage>
+mitk::StatisticsImageFilter<TInputImage>::~StatisticsImageFilter()
+{
+}
+
+template <typename TInputImage>
+typename mitk::StatisticsImageFilter<TInputImage>::DataObjectPointer mitk::StatisticsImageFilter<TInputImage>::MakeOutput(const DataObjectIdentifierType& name)
+{
+ if (name == "Minimum" ||
+ name == "Maximum")
+ {
+ return PixelObjectType::New();
+ }
+
+ if (name == "Mean" ||
+ name == "Sigma" ||
+ name == "Variance" ||
+ name == "Sum" ||
+ name == "SumOfSquares" ||
+ name == "SumOfCubes" ||
+ name == "SumOfQuadruples" ||
+ name == "Skewness" ||
+ name == "Kurtosis" ||
+ name == "MPP" ||
+ name == "Entropy" ||
+ name == "Uniformity" ||
+ name == "UPP" ||
+ name == "Median")
+ {
+ return RealObjectType::New();
+ }
+
+ if (name == "Histogram")
+ {
+ return HistogramType::New();
+ }
+
+ return Superclass::MakeOutput(name);
+}
+
+template <typename TInputImage>
+void mitk::StatisticsImageFilter<TInputImage>::SetHistogramParameters(unsigned int size, RealType lowerBound, RealType upperBound)
+{
+ bool modified = false;
+
+ if (m_HistogramSize != size)
+ {
+ m_HistogramSize = size;
+ modified = true;
+ }
+
+ if (m_HistogramLowerBound != lowerBound)
+ {
+ m_HistogramLowerBound = lowerBound;
+ modified = true;
+ }
+
+ if (m_HistogramUpperBound != upperBound)
+ {
+ m_HistogramUpperBound = upperBound;
+ modified = true;
+ }
+
+ m_ComputeHistogram = true;
+
+ if (modified)
+ this->Modified();
+}
+
+template <typename TInputImage>
+auto mitk::StatisticsImageFilter<TInputImage>::CreateInitializedHistogram() const -> HistogramPointer
+{
+ typename HistogramType::SizeType size;
+ size.SetSize(1);
+ size.Fill(m_HistogramSize);
+
+ typename HistogramType::MeasurementVectorType lowerBound;
+ lowerBound.SetSize(1);
+ lowerBound.Fill(m_HistogramLowerBound);
+
+ typename HistogramType::MeasurementVectorType upperBound;
+ upperBound.SetSize(1);
+ upperBound.Fill(m_HistogramUpperBound);
+
+ auto histogram = HistogramType::New();
+ histogram->SetMeasurementVectorSize(1);
+ histogram->Initialize(size, lowerBound, upperBound);
+
+ return histogram;
+}
+
+template <typename TInputImage>
+void mitk::StatisticsImageFilter<TInputImage>::BeforeStreamedGenerateData()
+{
+ Superclass::BeforeStreamedGenerateData();
+
+ m_Sum = 0;
+ m_SumOfPositivePixels = 0;
+ m_SumOfSquares = 0;
+ m_SumOfCubes = 0;
+ m_SumOfQuadruples = 0;
+ m_Count = 0;
+ m_CountOfPositivePixels = 0;
+ m_Min = itk::NumericTraits<PixelType>::max();
+ m_Max = itk::NumericTraits<PixelType>::NonpositiveMin();
+
+ if (m_ComputeHistogram)
+ m_Histogram = this->CreateInitializedHistogram();
+}
+
+template <typename TInputImage>
+void mitk::StatisticsImageFilter<TInputImage>::ThreadedStreamedGenerateData(const RegionType& regionForThread)
+{
+ itk::CompensatedSummation<RealType> sum = 0;
+ itk::CompensatedSummation<RealType> sumOfPositivePixels = 0;
+ itk::CompensatedSummation<RealType> sumOfSquares = 0;
+ itk::CompensatedSummation<RealType> sumOfCubes = 0;
+ itk::CompensatedSummation<RealType> sumOfQuadruples = 0;
+ itk::SizeValueType count = 0;
+ itk::SizeValueType countOfPositivePixels = 0;
+ auto min = itk::NumericTraits<PixelType>::max();
+ auto max = itk::NumericTraits<PixelType>::NonpositiveMin();
+ RealType realValue = 0;
+ RealType squareValue = 0;
+
+ HistogramPointer histogram;
+ typename HistogramType::MeasurementVectorType histogramMeasurement;
+ typename HistogramType::IndexType histogramIndex;
+
+ if (m_ComputeHistogram) // Initialize histogram
+ {
+ histogram = this->CreateInitializedHistogram();
+ histogramMeasurement.SetSize(1);
+ }
+
+ itk::ImageScanlineConstIterator<TInputImage> it(this->GetInput(), regionForThread);
+
+ while (!it.IsAtEnd())
+ {
+ while (!it.IsAtEndOfLine())
+ {
+ const auto& value = it.Get();
+ realValue = static_cast<RealType>(value);
+
+ if (m_ComputeHistogram) // Compute histogram
+ {
+ histogramMeasurement[0] = realValue;
+ histogram->GetIndex(histogramMeasurement, histogramIndex);
+ histogram->IncreaseFrequencyOfIndex(histogramIndex, 1);
+ }
+
+ min = std::min(min, value);
+ max = std::max(max, value);
+ squareValue = realValue * realValue;
+
+ sum += realValue;
+ sumOfSquares += squareValue;
+ sumOfCubes += squareValue * realValue;
+ sumOfQuadruples += squareValue * squareValue;
+ ++count;
+
+ if (0 < realValue)
+ {
+ sumOfPositivePixels += realValue;
+ ++countOfPositivePixels;
+ }
+
+ ++it;
+ }
+
+ it.NextLine();
+ }
+
+ std::lock_guard<std::mutex> mutexHolder(m_Mutex);
+
+ if (m_ComputeHistogram) // Merge histograms
+ {
+ typename HistogramType::ConstIterator histogramIt = histogram->Begin();
+ typename HistogramType::ConstIterator histogramEnd = histogram->End();
+
+ while (histogramIt != histogramEnd)
+ {
+ m_Histogram->GetIndex(histogramIt.GetMeasurementVector(), histogramIndex);
+ m_Histogram->IncreaseFrequencyOfIndex(histogramIndex, histogramIt.GetFrequency());
+ ++histogramIt;
+ }
+ }
+
+ m_Sum += sum;
+ m_SumOfPositivePixels += sumOfPositivePixels;
+ m_SumOfSquares += sumOfSquares;
+ m_SumOfCubes += sumOfCubes;
+ m_SumOfQuadruples += sumOfQuadruples;
+ m_Count += count;
+ m_CountOfPositivePixels += countOfPositivePixels;
+ m_Min = std::min(min, m_Min);
+ m_Max = std::max(max, m_Max);
+}
+
+template <typename TInputImage>
+void mitk::StatisticsImageFilter<TInputImage>::AfterStreamedGenerateData()
+{
+ Superclass::AfterStreamedGenerateData();
+
+ const RealType sum = m_Sum.GetSum();
+ const RealType sumOfPositivePixels = m_SumOfPositivePixels.GetSum();
+ const RealType sumOfSquares = m_SumOfSquares.GetSum();
+ const RealType sumOfCubes = m_SumOfCubes.GetSum();
+ const RealType sumOfQuadruples = m_SumOfQuadruples.GetSum();
+ const itk::SizeValueType count = m_Count;
+ const itk::SizeValueType countOfPositivePixels = m_CountOfPositivePixels;
+ const PixelType minimum = m_Min;
+ const PixelType maximum = m_Max;
+
+ const RealType mean = sum / static_cast<RealType>(count);
+ const RealType variance = (sumOfSquares - (sum * sum / static_cast<RealType>(count))) / (static_cast<RealType>(count) - 1);
+ const RealType sigma = std::sqrt(variance);
+
+ const RealType secondMoment = sumOfSquares / static_cast<RealType>(count);
+ const RealType thirdMoment = sumOfCubes / static_cast<RealType>(count);
+ const RealType fourthMoment = sumOfQuadruples / static_cast<RealType>(count);
+ const RealType skewness = (thirdMoment - 3 * secondMoment * mean + 2 * std::pow(mean, 3)) / std::pow(secondMoment - std::pow(mean, 2), 1.5);
+ const RealType kurtosis = (fourthMoment - 4 * thirdMoment * mean + 6 * secondMoment * std::pow(mean, 2) - 3 * std::pow(mean, 4)) / std::pow(secondMoment - std::pow(mean, 2), 2);
+ const RealType meanOfPositivePixels = sumOfPositivePixels / static_cast<RealType>(countOfPositivePixels);
+
+ this->SetMinimum(minimum);
+ this->SetMaximum(maximum);
+ this->SetMean(mean);
+ this->SetSigma(sigma);
+ this->SetVariance(variance);
+ this->SetSum(sum);
+ this->SetSumOfSquares(sumOfSquares);
+ this->SetSumOfCubes(sumOfCubes);
+ this->SetSumOfQuadruples(sumOfQuadruples);
+ this->SetSkewness(skewness);
+ this->SetKurtosis(kurtosis);
+ this->SetMPP(meanOfPositivePixels);
+
+ if (m_ComputeHistogram)
+ {
+ this->SetHistogram(m_Histogram);
+
+ mitk::HistogramStatisticsCalculator histogramStatisticsCalculator;
+ histogramStatisticsCalculator.SetHistogram(m_Histogram);
+ histogramStatisticsCalculator.CalculateStatistics();
+
+ this->SetEntropy(histogramStatisticsCalculator.GetEntropy());
+ this->SetUniformity(histogramStatisticsCalculator.GetUniformity());
+ this->SetUPP(histogramStatisticsCalculator.GetUPP());
+ this->SetMedian(histogramStatisticsCalculator.GetMedian());
+ }
+}
+
+template <typename TInputImage>
+void mitk::StatisticsImageFilter<TInputImage>::PrintSelf(std::ostream& os, itk::Indent indent) const
+{
+ Superclass::PrintSelf(os, indent);
+
+ os << indent << "SumOfCubes: " << this->GetSumOfCubes() << std::endl;
+ os << indent << "SumOfQuadruples: " << this->GetSumOfQuadruples() << std::endl;
+ os << indent << "Skewness: " << this->GetSkewness() << std::endl;
+ os << indent << "Kurtosis: " << this->GetKurtosis() << std::endl;
+ os << indent << "MPP: " << this->GetMPP() << std::endl;
+ os << indent << "Entropy: " << this->GetEntropy() << std::endl;
+ os << indent << "Uniformity: " << this->GetUniformity() << std::endl;
+ os << indent << "UPP: " << this->GetUPP() << std::endl;
+ os << indent << "Median: " << this->GetMedian() << std::endl;
+}
+
+#endif
diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.cpp b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.cpp
index 56bb7ac961..738628cf24 100644
--- a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.cpp
+++ b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.cpp
@@ -1,425 +1,424 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "QmitkImageStatisticsTreeModel.h"
#include "QmitkImageStatisticsTreeItem.h"
-#include "itkMutexLockHolder.h"
#include "mitkImageStatisticsContainerManager.h"
#include "mitkProportionalTimeGeometry.h"
#include "mitkStatisticsToImageRelationRule.h"
#include "mitkStatisticsToMaskRelationRule.h"
#include "QmitkStyleManager.h"
QmitkImageStatisticsTreeModel::QmitkImageStatisticsTreeModel(QObject *parent) : QmitkAbstractDataStorageModel(parent)
{
m_RootItem = new QmitkImageStatisticsTreeItem();
}
QmitkImageStatisticsTreeModel ::~QmitkImageStatisticsTreeModel()
{
// set data storage to nullptr so that the event listener gets removed
this->SetDataStorage(nullptr);
delete m_RootItem;
};
void QmitkImageStatisticsTreeModel::DataStorageChanged()
{
emit beginResetModel();
UpdateByDataStorage();
emit endResetModel();
emit modelChanged();
}
void QmitkImageStatisticsTreeModel::NodePredicateChanged()
{
emit beginResetModel();
UpdateByDataStorage();
emit endResetModel();
emit modelChanged();
}
int QmitkImageStatisticsTreeModel::columnCount(const QModelIndex& /*parent*/) const
{
int columns = m_StatisticNames.size() + 1;
return columns;
}
int QmitkImageStatisticsTreeModel::rowCount(const QModelIndex &parent) const
{
QmitkImageStatisticsTreeItem *parentItem;
if (parent.column() > 0)
return 0;
if (!parent.isValid())
parentItem = m_RootItem;
else
parentItem = static_cast<QmitkImageStatisticsTreeItem *>(parent.internalPointer());
return parentItem->childCount();
}
QVariant QmitkImageStatisticsTreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
QmitkImageStatisticsTreeItem* item = static_cast<QmitkImageStatisticsTreeItem*>(index.internalPointer());
if (role == Qt::DisplayRole)
{
return item->data(index.column());
}
else if (role == Qt::DecorationRole && index.column() == 0 && item->isWIP() && item->childCount()==0)
{
return QVariant(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/hourglass-half-solid.svg")));
}
return QVariant();
}
QModelIndex QmitkImageStatisticsTreeModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
QmitkImageStatisticsTreeItem *parentItem;
if (!parent.isValid())
parentItem = m_RootItem;
else
parentItem = static_cast<QmitkImageStatisticsTreeItem *>(parent.internalPointer());
QmitkImageStatisticsTreeItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex QmitkImageStatisticsTreeModel::parent(const QModelIndex &child) const
{
if (!child.isValid())
return QModelIndex();
QmitkImageStatisticsTreeItem *childItem = static_cast<QmitkImageStatisticsTreeItem *>(child.internalPointer());
QmitkImageStatisticsTreeItem *parentItem = childItem->parentItem();
if (parentItem == m_RootItem)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
Qt::ItemFlags QmitkImageStatisticsTreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return nullptr;
return QAbstractItemModel::flags(index);
}
QVariant QmitkImageStatisticsTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if ((Qt::DisplayRole == role) && (Qt::Horizontal == orientation))
{
if (section == 0)
{
return m_HeaderFirstColumn;
}
else
{
return QVariant(m_StatisticNames.at(section - 1).c_str());
}
}
return QVariant();
}
void QmitkImageStatisticsTreeModel::SetImageNodes(const std::vector<mitk::DataNode::ConstPointer> &nodes)
{
std::vector<std::pair<mitk::DataNode::ConstPointer, unsigned int>> tempNodes;
for (const auto &node : nodes)
{
auto data = node->GetData();
if (data)
{
auto timeSteps = data->GetTimeSteps();
for (unsigned int i = 0; i < timeSteps; i++)
{
tempNodes.push_back(std::make_pair(node, i));
}
}
}
emit beginResetModel();
m_TimeStepResolvedImageNodes = std::move(tempNodes);
m_ImageNodes = nodes;
UpdateByDataStorage();
emit endResetModel();
emit modelChanged();
}
void QmitkImageStatisticsTreeModel::SetMaskNodes(const std::vector<mitk::DataNode::ConstPointer> &nodes)
{
std::vector<std::pair<mitk::DataNode::ConstPointer, unsigned int>> tempNodes;
for (const auto &node : nodes)
{
auto data = node->GetData();
if (data)
{
auto timeSteps = data->GetTimeSteps();
// special case: apply one mask to each timestep of an 4D image
if (timeSteps == 1 && m_TimeStepResolvedImageNodes.size() > 1)
{
timeSteps = m_TimeStepResolvedImageNodes.size();
}
for (unsigned int i = 0; i < timeSteps; i++)
{
tempNodes.push_back(std::make_pair(node, i));
}
}
}
emit beginResetModel();
m_TimeStepResolvedMaskNodes = std::move(tempNodes);
m_MaskNodes = nodes;
UpdateByDataStorage();
emit endResetModel();
emit modelChanged();
}
void QmitkImageStatisticsTreeModel::Clear()
{
emit beginResetModel();
m_Statistics.clear();
m_ImageNodes.clear();
m_TimeStepResolvedImageNodes.clear();
m_MaskNodes.clear();
m_StatisticNames.clear();
emit endResetModel();
emit modelChanged();
}
void QmitkImageStatisticsTreeModel::SetIgnoreZeroValueVoxel(bool _arg)
{
if (m_IgnoreZeroValueVoxel != _arg)
{
emit beginResetModel();
m_IgnoreZeroValueVoxel = _arg;
UpdateByDataStorage();
emit endResetModel();
emit modelChanged();
}
}
bool QmitkImageStatisticsTreeModel::GetIgnoreZeroValueVoxel() const
{
return this->m_IgnoreZeroValueVoxel;
}
void QmitkImageStatisticsTreeModel::SetHistogramNBins(unsigned int nbins)
{
if (m_HistogramNBins != nbins)
{
emit beginResetModel();
m_HistogramNBins = nbins;
UpdateByDataStorage();
emit endResetModel();
emit modelChanged();
}
}
unsigned int QmitkImageStatisticsTreeModel::GetHistogramNBins() const
{
return this->m_HistogramNBins;
}
void QmitkImageStatisticsTreeModel::UpdateByDataStorage()
{
StatisticsContainerVector newStatistics;
auto datamanager = m_DataStorage.Lock();
if (datamanager.IsNotNull())
{
for (const auto &image : m_ImageNodes)
{
if (m_MaskNodes.empty())
{
auto stats = mitk::ImageStatisticsContainerManager::GetImageStatistics(datamanager, image->GetData(), nullptr, m_IgnoreZeroValueVoxel, m_HistogramNBins, true, false);
if (stats.IsNotNull())
{
newStatistics.emplace_back(stats);
}
}
else
{
for (const auto &mask : m_MaskNodes)
{
auto stats =
mitk::ImageStatisticsContainerManager::GetImageStatistics(datamanager, image->GetData(), mask->GetData(), m_IgnoreZeroValueVoxel, m_HistogramNBins, true, false);
if (stats.IsNotNull())
{
newStatistics.emplace_back(stats);
}
}
}
}
if (!newStatistics.empty())
{
emit dataAvailable();
}
}
{
- itk::MutexLockHolder<itk::SimpleFastMutexLock> locked(m_Mutex);
+ std::lock_guard<std::mutex> locked(m_Mutex);
m_Statistics = newStatistics;
}
m_StatisticNames = mitk::GetAllStatisticNames(m_Statistics);
BuildHierarchicalModel();
}
void QmitkImageStatisticsTreeModel::BuildHierarchicalModel()
{
// reset old model
delete m_RootItem;
m_RootItem = new QmitkImageStatisticsTreeItem();
bool hasMask = false;
bool hasMultipleTimesteps = false;
std::map<mitk::DataNode::ConstPointer, QmitkImageStatisticsTreeItem *> dataNodeToTreeItem;
for (const auto &statistic : m_Statistics)
{
bool isWIP = statistic->GetProperty(mitk::STATS_GENERATION_STATUS_PROPERTY_NAME.c_str()).IsNotNull();
// get the connected image data node/mask data node
auto imageRule = mitk::StatisticsToImageRelationRule::New();
auto imageOfStatisticsPredicate = imageRule->GetDestinationsDetector(statistic);
auto imageFinding = std::find_if(m_ImageNodes.begin(), m_ImageNodes.end(), [&imageOfStatisticsPredicate](const mitk::DataNode::ConstPointer& testNode) { return imageOfStatisticsPredicate->CheckNode(testNode); });
auto maskRule = mitk::StatisticsToMaskRelationRule::New();
auto maskOfStatisticsPredicate = maskRule->GetDestinationsDetector(statistic);
auto maskFinding = std::find_if(m_MaskNodes.begin(), m_MaskNodes.end(), [&maskOfStatisticsPredicate](const mitk::DataNode::ConstPointer& testNode) { return maskOfStatisticsPredicate->CheckNode(testNode); });
if (imageFinding == m_ImageNodes.end())
{
mitkThrow() << "no image found connected to statistic" << statistic << " Aborting.";
}
auto& image = *imageFinding;
// image: 1. hierarchy level
QmitkImageStatisticsTreeItem *imageItem = nullptr;
auto search = dataNodeToTreeItem.find(image);
// the tree item was created previously
if (search != dataNodeToTreeItem.end())
{
imageItem = search->second;
}
// create the tree item
else
{
QString imageLabel = QString::fromStdString(image->GetName());
if (statistic->GetTimeSteps() == 1 && maskFinding == m_MaskNodes.end())
{
auto statisticsObject = statistic->GetStatisticsForTimeStep(0);
imageItem = new QmitkImageStatisticsTreeItem(statisticsObject, m_StatisticNames, imageLabel, isWIP, m_RootItem);
}
else
{
imageItem = new QmitkImageStatisticsTreeItem(m_StatisticNames, imageLabel, isWIP, m_RootItem);
}
m_RootItem->appendChild(imageItem);
dataNodeToTreeItem.emplace(image, imageItem);
}
// mask: 2. hierarchy level (optional, only if mask exists)
QmitkImageStatisticsTreeItem *lastParent = nullptr;
if (maskFinding != m_MaskNodes.end())
{
auto& mask = *maskFinding;
QString maskLabel = QString::fromStdString(mask->GetName());
QmitkImageStatisticsTreeItem *maskItem;
// add statistical values directly in this hierarchy level
if (statistic->GetTimeSteps() == 1)
{
auto statisticsObject = statistic->GetStatisticsForTimeStep(0);
maskItem = new QmitkImageStatisticsTreeItem(statisticsObject, m_StatisticNames, maskLabel, isWIP, imageItem);
}
else
{
maskItem = new QmitkImageStatisticsTreeItem(m_StatisticNames, maskLabel, isWIP, imageItem);
}
imageItem->appendChild(maskItem);
lastParent = maskItem;
hasMask = true;
}
else
{
lastParent = imageItem;
}
// 3. hierarchy level (optional, only if >1 timestep)
if (statistic->GetTimeSteps() > 1)
{
for (unsigned int i = 0; i < statistic->GetTimeSteps(); i++)
{
QString timeStepLabel = "[" + QString::number(i) + "] " +
QString::number(statistic->GetTimeGeometry()->TimeStepToTimePoint(i)) + " ms";
if (statistic->TimeStepExists(i))
{
auto statisticsItem = new QmitkImageStatisticsTreeItem(
statistic->GetStatisticsForTimeStep(i), m_StatisticNames, timeStepLabel, isWIP, lastParent);
lastParent->appendChild(statisticsItem);
}
}
hasMultipleTimesteps = true;
}
}
QString headerString = "Images";
if (hasMask)
{
headerString += "/Masks";
}
if (hasMultipleTimesteps)
{
headerString += "/Timesteps";
}
m_HeaderFirstColumn = headerString;
}
void QmitkImageStatisticsTreeModel::NodeRemoved(const mitk::DataNode *)
{
emit beginResetModel();
UpdateByDataStorage();
emit endResetModel();
emit modelChanged();
}
void QmitkImageStatisticsTreeModel::NodeAdded(const mitk::DataNode *)
{
emit beginResetModel();
UpdateByDataStorage();
emit endResetModel();
emit modelChanged();
}
void QmitkImageStatisticsTreeModel::NodeChanged(const mitk::DataNode *)
{
emit beginResetModel();
UpdateByDataStorage();
emit endResetModel();
emit modelChanged();
}
diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.h b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.h
index 800959cba8..1d406558cc 100644
--- a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.h
+++ b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.h
@@ -1,127 +1,127 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef QmitkImageStatisticsTreeModel_h
#define QmitkImageStatisticsTreeModel_h
-#include "itkSimpleFastMutexLock.h"
-
#include "QmitkAbstractDataStorageModel.h"
//MITK
#include <MitkImageStatisticsUIExports.h>
#include "mitkImageStatisticsContainer.h"
+#include <mutex>
+
class QmitkImageStatisticsTreeItem;
/*!
\class QmitkImageStatisticsTreeModel
The class is used to represent the information of mitk::ImageStatisticsContainer in the set datastorage in the context of the QT view-model-concept.
The represented ImageStatisticContainer are specified by setting the image and mask nodes that should be regarded.
In addition you may specified the statistic computation property HistorgramNBins and IgnoreZeroValueVoxel to select the correct
statistics.
*/
class MITKIMAGESTATISTICSUI_EXPORT QmitkImageStatisticsTreeModel : public QmitkAbstractDataStorageModel
{
Q_OBJECT
public:
QmitkImageStatisticsTreeModel(QObject *parent = nullptr);
~QmitkImageStatisticsTreeModel() override;
void SetImageNodes(const std::vector<mitk::DataNode::ConstPointer>& nodes);
void SetMaskNodes(const std::vector<mitk::DataNode::ConstPointer>& nodes);
void Clear();
/*! /brief Set flag to ignore zero valued voxels */
void SetIgnoreZeroValueVoxel(bool _arg);
/*! /brief Get status of zero value voxel ignoring. */
bool GetIgnoreZeroValueVoxel() const;
/*! /brief Set bin size for histogram resolution.*/
void SetHistogramNBins(unsigned int nbins);
/*! /brief Get bin size for histogram resolution.*/
unsigned int GetHistogramNBins() const;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
signals:
void dataAvailable();
/** Is emitted whenever the model changes are finished (usually a bit later than dataAvailable()).*/
void modelChanged();
protected:
/*
* @brief See 'QmitkAbstractDataStorageModel'
*/
void DataStorageChanged() override;
/*
* @brief See 'QmitkAbstractDataStorageModel'
*/
void NodePredicateChanged() override;
/*
* @brief See 'QmitkAbstractDataStorageModel'
*/
void NodeAdded(const mitk::DataNode *node) override;
/*
* @brief See 'QmitkAbstractDataStorageModel'
*/
void NodeChanged(const mitk::DataNode *node) override;
/*
* @brief See 'QmitkAbstractDataStorageModel'
*/
void NodeRemoved(const mitk::DataNode *node) override;
private:
void UpdateByDataStorage();
using StatisticsContainerVector = std::vector<mitk::ImageStatisticsContainer::ConstPointer>;
/* builds a hierarchical tree model for the image statistics
1. Level: Image
--> 2. Level: Mask [if exist]
--> 3. Level: Timestep [if >1 exist] */
void BuildHierarchicalModel();
StatisticsContainerVector m_Statistics;
/** Relevant images set by the user.*/
std::vector<mitk::DataNode::ConstPointer> m_ImageNodes;
/** Helper that is constructed when m_ImageNodes is set. It has the same order
like m_ImageNodes, but each image is represented n times, while n is the number
of time steps the respective image has. This structure makes the business logic
to select the correct image given a QIndex much simpler and therefore easy to
understand/maintain. */
std::vector<std::pair<mitk::DataNode::ConstPointer, unsigned int> > m_TimeStepResolvedImageNodes;
/** relevant masks set by the user.*/
std::vector<mitk::DataNode::ConstPointer> m_MaskNodes;
/** @sa m_TimeStepResolvedImageNodes */
std::vector<std::pair<mitk::DataNode::ConstPointer, unsigned int>> m_TimeStepResolvedMaskNodes;
std::vector<std::string> m_StatisticNames;
- itk::SimpleFastMutexLock m_Mutex;
+ std::mutex m_Mutex;
QmitkImageStatisticsTreeItem *m_RootItem;
QVariant m_HeaderFirstColumn;
bool m_IgnoreZeroValueVoxel = false;
unsigned int m_HistogramNBins = 100;
};
#endif // mitkQmitkImageStatisticsTreeModel_h
diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkIntensityProfileVisualizationWidget.cpp b/Modules/ImageStatisticsUI/Qmitk/QmitkIntensityProfileVisualizationWidget.cpp
index aa12818e91..c42d862582 100644
--- a/Modules/ImageStatisticsUI/Qmitk/QmitkIntensityProfileVisualizationWidget.cpp
+++ b/Modules/ImageStatisticsUI/Qmitk/QmitkIntensityProfileVisualizationWidget.cpp
@@ -1,113 +1,113 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "QmitkIntensityProfileVisualizationWidget.h"
#include <QClipboard>
QmitkIntensityProfileVisualizationWidget::QmitkIntensityProfileVisualizationWidget(QWidget* parent) : QWidget(parent)
{
m_Controls.setupUi(this);
m_Controls.checkBoxShowSubchart->setChecked(false);
CreateConnections();
}
void QmitkIntensityProfileVisualizationWidget::SetIntensityProfile(mitk::IntensityProfile::ConstPointer intensityProfile, const std::string& dataLabel)
{
if (intensityProfile == nullptr)
return;
m_IntensityProfileList = mitk::CreateVectorFromIntensityProfile(intensityProfile);
if (m_IntensityProfileList.empty())
return;
m_Controls.chartWidget->AddData1D(m_IntensityProfileList, dataLabel);
m_Controls.chartWidget->SetChartType(dataLabel, QmitkChartWidget::ChartType::line);
m_Controls.chartWidget->SetXAxisLabel("Distance");
m_Controls.chartWidget->SetYAxisLabel("Intensity");
m_Controls.chartWidget->SetShowLegend(false);
m_Controls.chartWidget->Show(m_Controls.checkBoxShowSubchart->isChecked());
SetGUIElementsEnabled(true);
}
void QmitkIntensityProfileVisualizationWidget::Reset()
{
m_Controls.chartWidget->Clear();
SetGUIElementsEnabled(false);
m_IntensityProfileList.clear();
}
void QmitkIntensityProfileVisualizationWidget::SetTheme(QmitkChartWidget::ColorTheme style)
{
m_ChartStyle = style;
}
void QmitkIntensityProfileVisualizationWidget::CreateConnections()
{
connect(m_Controls.checkBoxShowSubchart, &QCheckBox::clicked, this, &QmitkIntensityProfileVisualizationWidget::OnShowSubchartCheckBoxChanged);
connect(m_Controls.buttonCopyToClipboard, &QPushButton::clicked, this, &QmitkIntensityProfileVisualizationWidget::OnClipboardButtonClicked);
connect(m_Controls.chartWidget, &QmitkChartWidget::PageSuccessfullyLoaded, this, &QmitkIntensityProfileVisualizationWidget::OnPageSuccessfullyLoaded);
}
void QmitkIntensityProfileVisualizationWidget::SetGUIElementsEnabled(bool enabled)
{
this->setEnabled(enabled);
m_Controls.groupBoxIntensityProfile->setEnabled(enabled);
m_Controls.groupBoxPlot->setEnabled(enabled);
m_Controls.buttonCopyToClipboard->setEnabled(enabled);
m_Controls.checkBoxShowSubchart->setEnabled(enabled);
m_Controls.chartWidget->setEnabled(enabled);
}
std::vector<double> QmitkIntensityProfileVisualizationWidget::ConvertIntensityProfileToVector(mitk::IntensityProfile::ConstPointer intensityProfile) const
{
std::vector<double> intensityProfileList;
if (intensityProfile != nullptr)
{
auto end = intensityProfile->End();
for (auto it = intensityProfile->Begin(); it != end; ++it)
{
intensityProfileList.push_back(it.GetMeasurementVector()[0]);
}
}
return intensityProfileList;
}
void QmitkIntensityProfileVisualizationWidget::OnClipboardButtonClicked()
{
if (m_IntensityProfileList.empty())
return;
QApplication::clipboard()->clear();
QString clipboard("Pixel \t Intensity\n");
for (unsigned int i = 0; i < m_IntensityProfileList.size(); i++)
{
clipboard = clipboard.append("%L1 \t %L2\n")
.arg(QString::number(i))
- .arg(QString::number(m_IntensityProfileList.at(i)));
+ .arg(m_IntensityProfileList.at(i),0,'f');
}
QApplication::clipboard()->setText(clipboard, QClipboard::Clipboard);
}
void QmitkIntensityProfileVisualizationWidget::OnShowSubchartCheckBoxChanged()
{
m_Controls.chartWidget->Show(m_Controls.checkBoxShowSubchart->isChecked());
}
void QmitkIntensityProfileVisualizationWidget::OnPageSuccessfullyLoaded()
{
m_Controls.chartWidget->SetTheme(m_ChartStyle);
}
diff --git a/Modules/ImageStatisticsUI/test/QmitkImageStatisticsDataGeneratorTest.cpp b/Modules/ImageStatisticsUI/test/QmitkImageStatisticsDataGeneratorTest.cpp
index df9e2f4846..4562aed61a 100644
--- a/Modules/ImageStatisticsUI/test/QmitkImageStatisticsDataGeneratorTest.cpp
+++ b/Modules/ImageStatisticsUI/test/QmitkImageStatisticsDataGeneratorTest.cpp
@@ -1,519 +1,537 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "QmitkImageStatisticsDataGenerator.h"
#include <QApplication>
#include <mitkStandaloneDataStorage.h>
#include "mitkImage.h"
#include "mitkPlanarFigure.h"
#include "mitkIOUtil.h"
#include "mitkStatisticsToImageRelationRule.h"
#include "mitkStatisticsToMaskRelationRule.h"
#include "mitkImageStatisticsContainerManager.h"
#include "mitkProperties.h"
#include "QmitkImageStatisticsCalculationRunnable.h"
#include <mitkTestFixture.h>
#include <mitkTestingMacros.h>
class TestQmitkImageStatisticsDataGenerator : public QmitkImageStatisticsDataGenerator
{
public:
TestQmitkImageStatisticsDataGenerator(mitk::DataStorage::Pointer storage, QObject* parent = nullptr) : QmitkImageStatisticsDataGenerator(storage, parent)
{
connect(this, &QmitkDataGeneratorBase::NewDataAvailable, this, &TestQmitkImageStatisticsDataGenerator::NewDataAvailableEmited);
connect(this, &QmitkDataGeneratorBase::DataGenerationStarted, this, &TestQmitkImageStatisticsDataGenerator::DataGenerationStartedEmited);
connect(this, &QmitkDataGeneratorBase::GenerationFinished, this, &TestQmitkImageStatisticsDataGenerator::GenerationFinishedEmited);
connect(this, &QmitkDataGeneratorBase::JobError, this, &TestQmitkImageStatisticsDataGenerator::JobErrorEmited);
};
mutable std::vector<mitk::DataStorage::SetOfObjects::ConstPointer> m_NewDataAvailable;
void NewDataAvailableEmited(mitk::DataStorage::SetOfObjects::ConstPointer data) const
{
m_NewDataAvailable.emplace_back(data);
};
mutable int m_DataGenerationStartedEmited = 0;
void DataGenerationStartedEmited(const mitk::DataNode* /*imageNode*/, const mitk::DataNode* /*roiNode*/, const QmitkDataGenerationJobBase* /*job*/) const
{
m_DataGenerationStartedEmited++;
}
mutable int m_GenerationFinishedEmited = 0;
void GenerationFinishedEmited() const
{
m_GenerationFinishedEmited++;
QCoreApplication::instance()->quit();
}
mutable std::vector<QString> m_JobErrorEmited_error;
void JobErrorEmited(QString error, const QmitkDataGenerationJobBase* /*failedJob*/) const
{
m_JobErrorEmited_error.emplace_back(error);
}
};
class QmitkImageStatisticsDataGeneratorTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(QmitkImageStatisticsDataGeneratorTestSuite);
MITK_TEST(GetterSetterTest);
MITK_TEST(NullTest);
MITK_TEST(OneImageTest);
MITK_TEST(MultiImageTest);
MITK_TEST(ImageAndROITest);
MITK_TEST(ImageAndMultiROITest);
MITK_TEST(MultiMultiTest);
MITK_TEST(InputChangedTest);
MITK_TEST(SettingsChangedTest);
MITK_TEST(DataStorageModificationTest);
CPPUNIT_TEST_SUITE_END();
mitk::DataStorage::Pointer m_DataStorage;
mitk::DataNode::Pointer m_ImageNode1;
mitk::DataNode::Pointer m_ImageNode2;
mitk::DataNode::Pointer m_MaskImageNode;
mitk::DataNode::Pointer m_PFNode;
mitk::Image::Pointer m_Image1;
mitk::Image::Pointer m_Image2;
mitk::Image::Pointer m_Mask;
mitk::PlanarFigure::Pointer m_PF;
QCoreApplication* m_TestApp;
public:
void setUp() override
{
m_DataStorage = mitk::StandaloneDataStorage::New();
m_ImageNode1 = mitk::DataNode::New();
m_ImageNode1->SetName("Image_1");
auto pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd");
m_Image1 = mitk::IOUtil::Load<mitk::Image>(pic3DCroppedFile);
CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Image1.IsNotNull());
m_ImageNode1->SetData(m_Image1);
m_DataStorage->Add(m_ImageNode1);
m_ImageNode2 = mitk::DataNode::New();
m_ImageNode2->SetName("Image_2");
m_Image2 = mitk::IOUtil::Load<mitk::Image>(pic3DCroppedFile);
CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Image2.IsNotNull());
m_ImageNode2->SetData(m_Image2);
m_DataStorage->Add(m_ImageNode2);
m_MaskImageNode = mitk::DataNode::New();
m_MaskImageNode->SetName("Mask");
auto pic3DCroppedBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedBinMask.nrrd");
m_Mask = mitk::IOUtil::Load<mitk::Image>(pic3DCroppedBinMaskFile);
CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D binary mask", m_Mask.IsNotNull());
m_MaskImageNode->SetData(m_Mask);
m_DataStorage->Add(m_MaskImageNode);
m_PFNode = mitk::DataNode::New();
m_PFNode->SetName("PF");
auto pic3DCroppedPlanarFigureFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedPF.pf");
m_PF = mitk::IOUtil::Load<mitk::PlanarFigure>(pic3DCroppedPlanarFigureFile);
CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D planar figure", m_PF.IsNotNull());
m_PFNode->SetData(m_PF);
m_DataStorage->Add(m_PFNode);
int argc = 0;
char** argv = nullptr;
m_TestApp = new QCoreApplication(argc, argv);
}
void tearDown() override
{
delete m_TestApp;
}
bool CheckResultNode(const std::vector<mitk::DataNode::Pointer> resultNodes, const mitk::DataNode* imageNode, const mitk::DataNode* roiNode, unsigned int histBin = 100, bool noZero = false)
{
for (auto& resultNode : resultNodes)
{
bool result = false;
if (resultNode && resultNode->GetData() && imageNode && imageNode->GetData())
{
auto imageRule = mitk::StatisticsToImageRelationRule::New();
result = !imageRule->GetRelationUIDs(resultNode, imageNode).empty();
if (roiNode)
{
auto maskRule = mitk::StatisticsToMaskRelationRule::New();
result = result && !maskRule->GetRelationUIDs(resultNode, roiNode).empty();
}
auto prop = resultNode->GetData()->GetProperty(mitk::STATS_HISTOGRAM_BIN_PROPERTY_NAME.c_str());
auto binProp = dynamic_cast<const mitk::UIntProperty*>(prop.GetPointer());
result = result && binProp->GetValue() == histBin;
prop = resultNode->GetData()->GetProperty(mitk::STATS_IGNORE_ZERO_VOXEL_PROPERTY_NAME.c_str());
auto zeroProp = dynamic_cast<const mitk::BoolProperty*>(prop.GetPointer());
result = result && zeroProp->GetValue() == noZero;
}
if (result)
{ //node was in the result set
return true;
}
}
return false;
}
void NullTest()
{
TestQmitkImageStatisticsDataGenerator generator(nullptr);
generator.Generate();
CPPUNIT_ASSERT_EQUAL(0, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(0, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(generator.m_NewDataAvailable.empty());
generator.SetDataStorage(m_DataStorage);
generator.Generate();
CPPUNIT_ASSERT_EQUAL(0, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(0, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(generator.m_NewDataAvailable.empty());
}
void GetterSetterTest()
{
TestQmitkImageStatisticsDataGenerator generator(nullptr);
CPPUNIT_ASSERT(nullptr == generator.GetDataStorage());
generator.SetDataStorage(m_DataStorage);
CPPUNIT_ASSERT(m_DataStorage == generator.GetDataStorage());
TestQmitkImageStatisticsDataGenerator generator2(m_DataStorage);
CPPUNIT_ASSERT(m_DataStorage == generator.GetDataStorage());
CPPUNIT_ASSERT_EQUAL(100u, generator.GetHistogramNBins());
CPPUNIT_ASSERT_EQUAL(false, generator.GetIgnoreZeroValueVoxel());
CPPUNIT_ASSERT_EQUAL(false, generator.GetAutoUpdate());
generator.SetHistogramNBins(3);
CPPUNIT_ASSERT_EQUAL(3u, generator.GetHistogramNBins());
CPPUNIT_ASSERT_EQUAL(false, generator.GetIgnoreZeroValueVoxel());
CPPUNIT_ASSERT_EQUAL(false, generator.GetAutoUpdate());
generator.SetIgnoreZeroValueVoxel(true);
CPPUNIT_ASSERT_EQUAL(3u, generator.GetHistogramNBins());
CPPUNIT_ASSERT_EQUAL(true, generator.GetIgnoreZeroValueVoxel());
CPPUNIT_ASSERT_EQUAL(false, generator.GetAutoUpdate());
generator.SetAutoUpdate(true);
CPPUNIT_ASSERT_EQUAL(3u, generator.GetHistogramNBins());
CPPUNIT_ASSERT_EQUAL(true, generator.GetIgnoreZeroValueVoxel());
CPPUNIT_ASSERT_EQUAL(true, generator.GetAutoUpdate());
}
void OneImageTest()
{
TestQmitkImageStatisticsDataGenerator generator(m_DataStorage);
+ QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes { m_ImageNode1 };
- generator.SetImageNodes({ m_ImageNode1 });
+ generator.SetImageNodes(imageNodes);
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(1, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(1 == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable.front()->front() }, m_ImageNode1, nullptr));
CPPUNIT_ASSERT_MESSAGE("Error: Rerun has generated new data.", generator.Generate());
}
void MultiImageTest()
{
TestQmitkImageStatisticsDataGenerator generator(m_DataStorage);
+ QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes { m_ImageNode1, m_ImageNode2 };
- generator.SetImageNodes({ m_ImageNode1, m_ImageNode2 });
+ generator.SetImageNodes(imageNodes);
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(2, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(2u == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front() }, m_ImageNode1, nullptr));
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front() }, m_ImageNode2, nullptr));
CPPUNIT_ASSERT_MESSAGE("Error: Rerun has generated new data.", generator.Generate());
}
void ImageAndROITest()
{
TestQmitkImageStatisticsDataGenerator generator(m_DataStorage);
+ QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes { m_ImageNode1 };
+ QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType roiNodes { m_MaskImageNode };
- generator.SetImageNodes({ m_ImageNode1 });
- generator.SetROINodes({ m_MaskImageNode });
+ generator.SetImageNodes(imageNodes);
+ generator.SetROINodes(roiNodes);
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(1, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(1 == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front() }, m_ImageNode1, m_MaskImageNode));
CPPUNIT_ASSERT_MESSAGE("Error: Rerun has generated new data.", generator.Generate());
- generator.SetROINodes({ m_PFNode });
+ roiNodes = { m_PFNode };
+
+ generator.SetROINodes(roiNodes);
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(2, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(2, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(2 == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[1]->front() }, m_ImageNode1, m_PFNode));
CPPUNIT_ASSERT_MESSAGE("Error: Rerun has generated new data.", generator.Generate());
}
void ImageAndMultiROITest()
{
TestQmitkImageStatisticsDataGenerator generator(m_DataStorage);
+ QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes{ m_ImageNode1 };
+ QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType roiNodes{ m_PFNode, m_MaskImageNode, nullptr };
- generator.SetImageNodes({ m_ImageNode1 });
- generator.SetROINodes({ m_PFNode, m_MaskImageNode, nullptr });
+ generator.SetImageNodes(imageNodes);
+ generator.SetROINodes(roiNodes);
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(3, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(3 == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(), generator.m_NewDataAvailable[2]->front()}, m_ImageNode1, m_PFNode));
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(), generator.m_NewDataAvailable[2]->front() }, m_ImageNode1, m_MaskImageNode));
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(), generator.m_NewDataAvailable[2]->front() }, m_ImageNode1, nullptr));
CPPUNIT_ASSERT_MESSAGE("Error: Rerun has generated new data.", generator.Generate());
}
void MultiMultiTest()
{
TestQmitkImageStatisticsDataGenerator generator(m_DataStorage);
+ QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes{ m_ImageNode1, m_ImageNode2 };
+ QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType roiNodes{ m_PFNode, m_MaskImageNode, nullptr };
- generator.SetImageNodes({ m_ImageNode1, m_ImageNode2 });
- generator.SetROINodes({ m_PFNode, m_MaskImageNode, nullptr });
+ generator.SetImageNodes(imageNodes);
+ generator.SetROINodes(roiNodes);
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(6, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(6 == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(),
generator.m_NewDataAvailable[2]->front(), generator.m_NewDataAvailable[3]->front(),
generator.m_NewDataAvailable[4]->front(), generator.m_NewDataAvailable[5]->front() }, m_ImageNode1, m_PFNode));
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(),
generator.m_NewDataAvailable[2]->front(), generator.m_NewDataAvailable[3]->front(),
generator.m_NewDataAvailable[4]->front(), generator.m_NewDataAvailable[5]->front() }, m_ImageNode1, m_MaskImageNode));
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(),
generator.m_NewDataAvailable[2]->front(), generator.m_NewDataAvailable[3]->front(),
generator.m_NewDataAvailable[4]->front(), generator.m_NewDataAvailable[5]->front() }, m_ImageNode1, nullptr));
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(),
generator.m_NewDataAvailable[2]->front(), generator.m_NewDataAvailable[3]->front(),
generator.m_NewDataAvailable[4]->front(), generator.m_NewDataAvailable[5]->front() }, m_ImageNode2, m_PFNode));
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(),
generator.m_NewDataAvailable[2]->front(), generator.m_NewDataAvailable[3]->front(),
generator.m_NewDataAvailable[4]->front(), generator.m_NewDataAvailable[5]->front() }, m_ImageNode2, m_MaskImageNode));
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(),
generator.m_NewDataAvailable[2]->front(), generator.m_NewDataAvailable[3]->front(),
generator.m_NewDataAvailable[4]->front(), generator.m_NewDataAvailable[5]->front() }, m_ImageNode2, nullptr));
CPPUNIT_ASSERT_MESSAGE("Error: Rerun has generated new data.", generator.Generate());
}
void InputChangedTest()
{
TestQmitkImageStatisticsDataGenerator generator(m_DataStorage);
+ QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes{ m_ImageNode2 };
- generator.SetImageNodes({ m_ImageNode2 });
+ generator.SetImageNodes(imageNodes);
m_TestApp->processEvents();
CPPUNIT_ASSERT_EQUAL(0, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(0, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(0 == generator.m_NewDataAvailable.size());
+ imageNodes = { m_ImageNode1 };
+
generator.SetAutoUpdate(true);
- generator.SetImageNodes({ m_ImageNode1 });
+ generator.SetImageNodes(imageNodes);
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 1, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 1, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", 1 == generator.m_NewDataAvailable.size());
- generator.SetImageNodes({ m_ImageNode1 });
+ generator.SetImageNodes(imageNodes);
m_TestApp->processEvents();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 1, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 1, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 1 == generator.m_NewDataAvailable.size());
+ QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType roiNodes{ m_MaskImageNode };
+
generator.SetAutoUpdate(true);
- generator.SetROINodes({ m_MaskImageNode });
+ generator.SetROINodes(roiNodes);
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 2, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 2, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", 2 == generator.m_NewDataAvailable.size());
}
void SettingsChangedTest()
{
TestQmitkImageStatisticsDataGenerator generator(m_DataStorage);
+ QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes{ m_ImageNode1 };
- generator.SetImageNodes({ m_ImageNode1 });
+ generator.SetImageNodes(imageNodes);
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(1, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(1 == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front() }, m_ImageNode1, nullptr));
generator.SetHistogramNBins(50);
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(2, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(2, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(2 == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[1]->front() }, m_ImageNode1, nullptr, 50));
generator.SetIgnoreZeroValueVoxel(true);
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(3, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(3, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(3 == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[2]->front() }, m_ImageNode1, nullptr, 50, true));
//now check auto update feature
generator.SetAutoUpdate(true);
generator.SetHistogramNBins(5);
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 4, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 4, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", 4 == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[3]->front() }, m_ImageNode1, nullptr, 5, true));
generator.SetHistogramNBins(5);
m_TestApp->processEvents();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 4, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 4, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 4 == generator.m_NewDataAvailable.size());
generator.SetIgnoreZeroValueVoxel(false);
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 5, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 5, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", 5 == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[4]->front() }, m_ImageNode1, nullptr, 5, false));
generator.SetIgnoreZeroValueVoxel(false);
m_TestApp->processEvents();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 5, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 5, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 5 == generator.m_NewDataAvailable.size());
}
void DataStorageModificationTest()
{
TestQmitkImageStatisticsDataGenerator generator(m_DataStorage);
+ QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes{ m_ImageNode1 };
+ QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType roiNodes{ m_PFNode };
- generator.SetImageNodes({ m_ImageNode1 });
- generator.SetROINodes({ m_PFNode });
+ generator.SetImageNodes(imageNodes);
+ generator.SetROINodes(roiNodes);
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(1, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(1 == generator.m_NewDataAvailable.size());
m_PF->Modified();
m_PFNode->Modified();
m_TestApp->processEvents();
CPPUNIT_ASSERT_EQUAL(1, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(1 == generator.m_NewDataAvailable.size());
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(2, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(2, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(2 == generator.m_NewDataAvailable.size());
//now check auto update feature
generator.SetAutoUpdate(true);
m_PF->Modified();
m_PFNode->Modified();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 3, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 3, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", 3 == generator.m_NewDataAvailable.size());
m_DataStorage->Add(mitk::DataNode::New());
m_TestApp->processEvents();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", 3, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", 3, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", 3 == generator.m_NewDataAvailable.size());
m_Image2->Modified();
m_ImageNode2->Modified();
m_TestApp->processEvents();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", 3, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", 3, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", 3 == generator.m_NewDataAvailable.size());
}
};
MITK_TEST_SUITE_REGISTRATION(QmitkImageStatisticsDataGenerator)
diff --git a/Modules/LegacyIO/mitkImageWriter.cpp b/Modules/LegacyIO/mitkImageWriter.cpp
index 3405fbb559..0a90c1770a 100644
--- a/Modules/LegacyIO/mitkImageWriter.cpp
+++ b/Modules/LegacyIO/mitkImageWriter.cpp
@@ -1,458 +1,458 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkImageWriter.h"
#include "mitkImage.h"
#include "mitkImageAccessByItk.h"
#include "mitkImageReadAccessor.h"
#include "mitkImageTimeSelector.h"
#include "mitkItkPictureWrite.h"
#include <mitkLocaleSwitch.h>
#include <itkImageIOBase.h>
#include <itkImageIOFactory.h>
+#include <vtkImageData.h>
+#include <vtkXMLImageDataWriter.h>
+
mitk::ImageWriter::ImageWriter() : m_UseCompression(true)
{
this->SetNumberOfRequiredInputs(1);
m_MimeType = "";
SetDefaultExtension();
}
mitk::ImageWriter::~ImageWriter()
{
}
void mitk::ImageWriter::SetFileName(const char *fileName)
{
if (fileName && (fileName == this->m_FileName))
{
return;
}
if (fileName)
{
this->m_FileName = fileName;
this->m_FileNameWithoutExtension = this->m_FileName;
this->m_Extension.clear();
std::size_t pos = this->m_FileName.find_last_of("/\\");
if (pos != std::string::npos)
{
std::size_t ppos = this->m_FileName.find_first_of('.', pos);
if (ppos != std::string::npos)
{
this->m_FileNameWithoutExtension = this->m_FileName.substr(0, ppos);
this->m_Extension = this->m_FileName.substr(ppos);
}
}
}
else
{
this->m_FileName.clear();
this->m_FileNameWithoutExtension.clear();
this->m_Extension.clear();
}
this->Modified();
}
void mitk::ImageWriter::SetFileName(const std::string &fileName)
{
this->SetFileName(fileName.c_str());
}
void mitk::ImageWriter::SetExtension(const char *extension)
{
if (extension && (extension == this->m_Extension))
{
return;
}
if (extension)
{
this->m_Extension = extension;
this->m_FileName = this->m_FileNameWithoutExtension + this->m_Extension;
}
else
{
this->m_Extension.clear();
this->m_FileName = this->m_FileNameWithoutExtension;
}
this->Modified();
}
void mitk::ImageWriter::SetExtension(const std::string &extension)
{
this->SetFileName(extension.c_str());
}
void mitk::ImageWriter::SetDefaultExtension()
{
this->m_Extension = ".mhd";
this->m_FileName = this->m_FileNameWithoutExtension + this->m_Extension;
this->Modified();
}
-#include <vtkConfigure.h>
-#include <vtkImageData.h>
-#include <vtkXMLImageDataWriter.h>
static void writeVti(const char *filename, mitk::Image *image, int t = 0)
{
vtkXMLImageDataWriter *vtkwriter = vtkXMLImageDataWriter::New();
vtkwriter->SetFileName(filename);
vtkwriter->SetInputData(image->GetVtkImageData(t));
vtkwriter->Write();
vtkwriter->Delete();
}
#include <itkRGBAPixel.h>
void mitk::ImageWriter::WriteByITK(mitk::Image *image, const std::string &fileName)
{
MITK_INFO << "Writing image: " << fileName << std::endl;
// Pictures and picture series like .png are written via a different mechanism then volume images.
// So, they are still multiplexed and thus not support vector images.
if (fileName.find(".png") != std::string::npos || fileName.find(".tif") != std::string::npos ||
fileName.find(".jpg") != std::string::npos || fileName.find(".bmp") != std::string::npos)
{
try
{
// switch processing of single/multi-component images
if (image->GetPixelType(0).GetNumberOfComponents() == 1)
{
AccessByItk_1(image, _mitkItkPictureWrite, fileName);
}
else
{
AccessFixedPixelTypeByItk_1(image,
_mitkItkPictureWriteComposite,
MITK_ACCESSBYITK_PIXEL_TYPES_SEQ MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES_SEQ,
fileName);
}
}
catch (itk::ExceptionObject &e)
{
std::cerr << "Caught " << e.what() << std::endl;
}
catch (std::exception &e)
{
std::cerr << "Caught std::exception " << e.what() << std::endl;
}
return;
}
// Implementation of writer using itkImageIO directly. This skips the use
// of templated itkImageFileWriter, which saves the multiplexing on MITK side.
unsigned int dimension = image->GetDimension();
unsigned int *dimensions = image->GetDimensions();
mitk::PixelType pixelType = image->GetPixelType();
mitk::Vector3D mitkSpacing = image->GetGeometry()->GetSpacing();
mitk::Point3D mitkOrigin = image->GetGeometry()->GetOrigin();
// Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, though they are not supported in MITK
itk::Vector<double, 4u> spacing4D;
spacing4D[0] = mitkSpacing[0];
spacing4D[1] = mitkSpacing[1];
spacing4D[2] = mitkSpacing[2];
spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have an valid value here
itk::Vector<double, 4u> origin4D;
origin4D[0] = mitkOrigin[0];
origin4D[1] = mitkOrigin[1];
origin4D[2] = mitkOrigin[2];
origin4D[3] = 0; // There is no support for a 4D origin. However, we should have an valid value here
itk::ImageIOBase::Pointer imageIO =
- itk::ImageIOFactory::CreateImageIO(fileName.c_str(), itk::ImageIOFactory::WriteMode);
+ itk::ImageIOFactory::CreateImageIO(fileName.c_str(), itk::IOFileModeEnum::WriteMode);
if (imageIO.IsNull())
{
itkExceptionMacro(<< "Error: Could not create itkImageIO via factory for file " << fileName);
}
// Set the necessary information for imageIO
imageIO->SetNumberOfDimensions(dimension);
imageIO->SetPixelType(pixelType.GetPixelType());
- imageIO->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ?
- static_cast<itk::ImageIOBase::IOComponentType>(pixelType.GetComponentType()) :
- itk::ImageIOBase::UNKNOWNCOMPONENTTYPE);
+ imageIO->SetComponentType(static_cast<int>(pixelType.GetComponentType()) < PixelComponentUserType
+ ? pixelType.GetComponentType()
+ : itk::IOComponentEnum::UNKNOWNCOMPONENTTYPE);
imageIO->SetNumberOfComponents(pixelType.GetNumberOfComponents());
itk::ImageIORegion ioRegion(dimension);
for (unsigned int i = 0; i < dimension; i++)
{
imageIO->SetDimensions(i, dimensions[i]);
imageIO->SetSpacing(i, spacing4D[i]);
imageIO->SetOrigin(i, origin4D[i]);
- mitk::Vector3D mitkDirection;
+ mitk::Vector3D mitkDirection(0.0);
mitkDirection.SetVnlVector(
- image->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i));
+ image->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i).as_ref());
itk::Vector<double, 4u> direction4D;
direction4D[0] = mitkDirection[0];
direction4D[1] = mitkDirection[1];
direction4D[2] = mitkDirection[2];
// MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must
// save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix.
if (i == 3)
direction4D[3] = 1; // homogenous component
else
direction4D[3] = 0;
vnl_vector<double> axisDirection(dimension);
for (unsigned int j = 0; j < dimension; j++)
{
axisDirection[j] = direction4D[j] / spacing4D[i];
}
imageIO->SetDirection(i, axisDirection);
ioRegion.SetSize(i, image->GetLargestPossibleRegion().GetSize(i));
ioRegion.SetIndex(i, image->GetLargestPossibleRegion().GetIndex(i));
}
// use compression if available
imageIO->SetUseCompression(m_UseCompression);
imageIO->SetIORegion(ioRegion);
imageIO->SetFileName(fileName);
ImageReadAccessor imageAccess(image);
imageIO->Write(imageAccess.GetData());
}
void mitk::ImageWriter::GenerateData()
{
mitk::LocaleSwitch localeSwitch("C");
if (m_FileName == "")
{
itkWarningMacro(<< "Sorry, filename has not been set!");
return;
}
FILE *tempFile = fopen(m_FileName.c_str(), "w");
if (tempFile == nullptr)
{
itkExceptionMacro(<< "File location not writeable");
return;
}
fclose(tempFile);
remove(m_FileName.c_str());
// Creating clone of input image, since i might change the geometry
mitk::Image::Pointer input = this->GetInput()->Clone();
// Check if geometry information will be lost
if (input->GetDimension() == 2)
{
if (!input->GetGeometry()->Is2DConvertable())
{
MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might "
"consider using Convert2Dto3DImageFilter before saving.";
// set matrix to identity
mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New();
affTrans->SetIdentity();
mitk::Vector3D spacing = input->GetGeometry()->GetSpacing();
mitk::Point3D origin = input->GetGeometry()->GetOrigin();
input->GetGeometry()->SetIndexToWorldTransform(affTrans);
input->GetGeometry()->SetSpacing(spacing);
input->GetGeometry()->SetOrigin(origin);
}
}
bool vti = (m_Extension.find(".vti") != std::string::npos);
// If the extension is NOT .nrrd and NOT .nii and NOT .nii.gz the following block is entered
if (m_Extension.find(".nrrd") == std::string::npos &&
m_Extension.find(".nii") == std::string::npos &&
m_Extension.find(".nii.gz") == std::string::npos)
{
if (input->GetDimension() > 3)
{
int t, timesteps;
timesteps = input->GetDimension(3);
ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New();
timeSelector->SetInput(input);
mitk::Image::Pointer image = timeSelector->GetOutput();
for (t = 0; t < timesteps; ++t)
{
std::ostringstream filename;
timeSelector->SetTimeNr(t);
timeSelector->Update();
if (input->GetTimeGeometry()->IsValidTimeStep(t))
{
const mitk::TimeBounds timebounds = input->GetTimeGeometry()->GetTimeBounds(t);
filename << m_FileNameWithoutExtension << "_S" << std::setprecision(0) << timebounds[0] << "_E"
<< std::setprecision(0) << timebounds[1] << "_T" << t << m_Extension;
}
else
{
itkWarningMacro(<< "Error on write: TimeGeometry invalid of image " << filename.str() << ".");
filename << m_FileNameWithoutExtension << "_T" << t << m_Extension;
}
if (vti)
{
writeVti(filename.str().c_str(), input, t);
}
else
{
WriteByITK(image, filename.str());
}
}
}
else if (vti)
{
writeVti(m_FileName.c_str(), input);
}
else
{
WriteByITK(input, m_FileName);
}
}
else
{
if (m_Extension.find(".nrrd") != std::string::npos ||
m_Extension.find(".nii") != std::string::npos ||
m_Extension.find(".nii.gz") != std::string::npos)
{
WriteByITK(input, this->m_FileName);
}
else
{
itkExceptionMacro(<< "File type not writeable");
}
}
m_MimeType = "application/MITK.Pic";
}
bool mitk::ImageWriter::CanWriteDataType(DataNode *input)
{
if (input)
{
return this->CanWriteBaseDataType(input->GetData());
}
return false;
}
void mitk::ImageWriter::SetInput(DataNode *input)
{
if (input && CanWriteDataType(input))
this->ProcessObject::SetNthInput(0, dynamic_cast<mitk::Image *>(input->GetData()));
}
std::string mitk::ImageWriter::GetWritenMIMEType()
{
return m_MimeType;
}
std::vector<std::string> mitk::ImageWriter::GetPossibleFileExtensions()
{
std::vector<std::string> possibleFileExtensions;
possibleFileExtensions.push_back(".bmp");
possibleFileExtensions.push_back(".dcm");
possibleFileExtensions.push_back(".DCM");
possibleFileExtensions.push_back(".dicom");
possibleFileExtensions.push_back(".DICOM");
possibleFileExtensions.push_back(".gipl");
possibleFileExtensions.push_back(".gipl.gz");
possibleFileExtensions.push_back(".mha");
possibleFileExtensions.push_back(".nii");
possibleFileExtensions.push_back(".nii.gz");
possibleFileExtensions.push_back(".nrrd");
possibleFileExtensions.push_back(".nhdr");
possibleFileExtensions.push_back(".png");
possibleFileExtensions.push_back(".PNG");
possibleFileExtensions.push_back(".spr");
possibleFileExtensions.push_back(".mhd");
possibleFileExtensions.push_back(".vtk");
possibleFileExtensions.push_back(".vti");
possibleFileExtensions.push_back(".hdr");
possibleFileExtensions.push_back(".img");
possibleFileExtensions.push_back(".img.gz");
possibleFileExtensions.push_back(".png");
possibleFileExtensions.push_back(".tif");
possibleFileExtensions.push_back(".jpg");
return possibleFileExtensions;
}
std::string mitk::ImageWriter::GetSupportedBaseData() const
{
return Image::GetStaticNameOfClass();
}
std::string mitk::ImageWriter::GetFileExtension()
{
return m_Extension;
}
void mitk::ImageWriter::SetInput(mitk::Image *image)
{
this->ProcessObject::SetNthInput(0, image);
}
const mitk::Image *mitk::ImageWriter::GetInput()
{
if (this->GetNumberOfInputs() < 1)
{
return nullptr;
}
else
{
return static_cast<const mitk::Image *>(this->ProcessObject::GetInput(0));
}
}
const char *mitk::ImageWriter::GetDefaultFilename()
{
return "Image.nrrd";
}
const char *mitk::ImageWriter::GetFileDialogPattern()
{
return "Nearly Raw Raster Data (*.nrrd);;"
"NIfTI format (*.nii *.nii.gz);;"
"VTK Image Data Files (*.vti);;"
"NRRD with detached header (*.nhdr);;"
"Analyze Format (*.hdr);;"
"MetaImage (*.mhd);;"
"Sets of 2D slices (*.png *.tiff *.jpg *.jpeg *.bmp);;"
"DICOM (*.dcm *.DCM *.dicom *.DICOM);;"
"UMDS GIPL Format Files (*.gipl *.gipl.gz)";
}
const char *mitk::ImageWriter::GetDefaultExtension()
{
return ".nrrd";
}
bool mitk::ImageWriter::CanWriteBaseDataType(BaseData::Pointer data)
{
return dynamic_cast<mitk::Image *>(data.GetPointer());
}
void mitk::ImageWriter::DoWrite(BaseData::Pointer data)
{
if (this->CanWriteBaseDataType(data))
{
this->SetInput(dynamic_cast<mitk::Image *>(data.GetPointer()));
this->Update();
}
}
void mitk::ImageWriter::SetUseCompression(bool useCompression)
{
m_UseCompression = useCompression;
}
diff --git a/Modules/LegacyIO/mitkItkImageFileReader.cpp b/Modules/LegacyIO/mitkItkImageFileReader.cpp
index ef09bea936..7cbd6e1fd0 100644
--- a/Modules/LegacyIO/mitkItkImageFileReader.cpp
+++ b/Modules/LegacyIO/mitkItkImageFileReader.cpp
@@ -1,214 +1,214 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkItkImageFileReader.h"
#include "mitkConfig.h"
#include "mitkException.h"
#include <mitkLocaleSwitch.h>
#include <mitkProportionalTimeGeometry.h>
#include <itkImage.h>
#include <itkImageFileReader.h>
#include <itksys/Directory.hxx>
#include <itksys/SystemTools.hxx>
//#include <itkImageSeriesReader.h>
#include <itkImageFileReader.h>
#include <itkImageIOFactory.h>
#include <itkImageIORegion.h>
#include <itkMetaDataObject.h>
//#include <itkImageSeriesReader.h>
//#include <itkDICOMImageIO2.h>
//#include <itkDICOMSeriesFileNames.h>
//#include <itkGDCMImageIO.h>
//#include <itkGDCMSeriesFileNames.h>
//#include <itkNumericSeriesFileNames.h>
void mitk::ItkImageFileReader::GenerateData()
{
mitk::LocaleSwitch localeSwitch("C");
mitk::Image::Pointer image = this->GetOutput();
const unsigned int MINDIM = 2;
const unsigned int MAXDIM = 4;
MITK_INFO("mitkItkImageFileReader") << "loading " << m_FileName << " via itk::ImageIOFactory... " << std::endl;
// Check to see if we can read the file given the name or prefix
if (m_FileName == "")
{
mitkThrow() << "Empty filename in mitk::ItkImageFileReader ";
return;
}
itk::ImageIOBase::Pointer imageIO =
- itk::ImageIOFactory::CreateImageIO(m_FileName.c_str(), itk::ImageIOFactory::ReadMode);
+ itk::ImageIOFactory::CreateImageIO(m_FileName.c_str(), itk::IOFileModeEnum::ReadMode);
if (imageIO.IsNull())
{
// itkWarningMacro( << "File Type not supported!" );
mitkThrow() << "Could not create itk::ImageIOBase object for filename " << m_FileName;
return;
}
// Got to allocate space for the image. Determine the characteristics of
// the image.
imageIO->SetFileName(m_FileName.c_str());
imageIO->ReadImageInformation();
unsigned int ndim = imageIO->GetNumberOfDimensions();
if (ndim < MINDIM || ndim > MAXDIM)
{
itkWarningMacro(<< "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim
<< " dimensions! Reading as 4D.");
ndim = MAXDIM;
}
itk::ImageIORegion ioRegion(ndim);
itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize();
itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex();
unsigned int dimensions[MAXDIM];
dimensions[0] = 0;
dimensions[1] = 0;
dimensions[2] = 0;
dimensions[3] = 0;
ScalarType spacing[MAXDIM];
spacing[0] = 1.0f;
spacing[1] = 1.0f;
spacing[2] = 1.0f;
spacing[3] = 1.0f;
Point3D origin;
origin.Fill(0);
unsigned int i;
for (i = 0; i < ndim; ++i)
{
ioStart[i] = 0;
ioSize[i] = imageIO->GetDimensions(i);
if (i < MAXDIM)
{
dimensions[i] = imageIO->GetDimensions(i);
spacing[i] = imageIO->GetSpacing(i);
if (spacing[i] <= 0)
spacing[i] = 1.0f;
}
if (i < 3)
{
origin[i] = imageIO->GetOrigin(i);
}
}
ioRegion.SetSize(ioSize);
ioRegion.SetIndex(ioStart);
MITK_INFO("mitkItkImageFileReader") << "ioRegion: " << ioRegion << std::endl;
imageIO->SetIORegion(ioRegion);
void *buffer = new unsigned char[imageIO->GetImageSizeInBytes()];
imageIO->Read(buffer);
image->Initialize(MakePixelType(imageIO), ndim, dimensions);
image->SetImportChannel(buffer, 0, Image::ManageMemory);
// access direction of itk::Image and include spacing
mitk::Matrix3D matrix;
matrix.SetIdentity();
unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim);
for (i = 0; i < itkDimMax3; ++i)
for (j = 0; j < itkDimMax3; ++j)
matrix[i][j] = imageIO->GetDirection(j)[i];
// re-initialize PlaneGeometry with origin and direction
PlaneGeometry *planeGeometry = static_cast<PlaneGeometry *>(image->GetSlicedGeometry(0)->GetPlaneGeometry(0));
planeGeometry->SetOrigin(origin);
planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix);
// re-initialize SlicedGeometry3D
SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0);
slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2));
slicedGeometry->SetSpacing(spacing);
MITK_INFO("mitkItkImageFileReader") << slicedGeometry->GetCornerPoint(false, false, false);
MITK_INFO("mitkItkImageFileReader") << slicedGeometry->GetCornerPoint(true, true, true);
// re-initialize TimeGeometry
ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New();
timeGeometry->Initialize(slicedGeometry, image->GetDimension(3));
image->SetTimeGeometry(timeGeometry);
buffer = nullptr;
MITK_INFO("mitkItkImageFileReader") << "number of image components: " << image->GetPixelType().GetNumberOfComponents()
<< std::endl;
// mitk::DataNode::Pointer node = this->GetOutput();
// node->SetData( image );
// add level-window property
// if ( image->GetPixelType().GetNumberOfComponents() == 1 )
//{
// SetDefaultImageProperties( node );
//}
MITK_INFO("mitkItkImageFileReader") << "...finished!" << std::endl;
}
bool mitk::ItkImageFileReader::CanReadFile(const std::string filename,
const std::string filePrefix,
const std::string filePattern)
{
// First check the extension
if (filename == "")
return false;
// check if image is serie
if (filePattern != "" && filePrefix != "")
return false;
itk::ImageIOBase::Pointer imageIO =
- itk::ImageIOFactory::CreateImageIO(filename.c_str(), itk::ImageIOFactory::ReadMode);
+ itk::ImageIOFactory::CreateImageIO(filename.c_str(), itk::IOFileModeEnum::ReadMode);
if (imageIO.IsNull())
return false;
try
{
imageIO->SetFileName(filename.c_str());
imageIO->ReadImageInformation();
itk::MetaDataDictionary imgMetaDictionary = imageIO->GetMetaDataDictionary();
std::vector<std::string> imgMetaKeys = imgMetaDictionary.GetKeys();
std::vector<std::string>::const_iterator itKey = imgMetaKeys.begin();
std::string metaString;
for (; itKey != imgMetaKeys.end(); itKey++)
{
itk::ExposeMetaData<std::string>(imgMetaDictionary, *itKey, metaString);
if (itKey->find("modality") != std::string::npos)
{
if (metaString.find("DWMRI") != std::string::npos)
{
return false; // DiffusionImageReader should handle this
}
}
}
}
catch (...)
{
MITK_INFO("mitkItkImageFileReader") << "Could not read ImageInformation ";
}
return true;
}
mitk::ItkImageFileReader::ItkImageFileReader() : m_FileName(""), m_FilePrefix(""), m_FilePattern("")
{
}
mitk::ItkImageFileReader::~ItkImageFileReader()
{
}
diff --git a/Modules/LegacyIO/mitkSurfaceVtkWriter.txx b/Modules/LegacyIO/mitkSurfaceVtkWriter.txx
index 3072e4634c..83de49a969 100644
--- a/Modules/LegacyIO/mitkSurfaceVtkWriter.txx
+++ b/Modules/LegacyIO/mitkSurfaceVtkWriter.txx
@@ -1,171 +1,170 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkSurfaceVtkWriter.h"
-#include <vtkConfigure.h>
#include <vtkErrorCode.h>
#include <vtkLinearTransform.h>
#include <vtkPolyData.h>
#include <vtkTransformPolyDataFilter.h>
#include <sstream>
#include <cstdio>
#include <sys/stat.h>
#include <sys/types.h>
template <class VTKWRITER>
mitk::SurfaceVtkWriter<VTKWRITER>::SurfaceVtkWriter() : m_WriterWriteHasReturnValue(false)
{
this->SetNumberOfRequiredInputs(1);
m_VtkWriter = vtkSmartPointer<VtkWriterType>::New();
// enable to write ascii-formatted-file
// m_VtkWriter->SetFileTypeToASCII();
SetDefaultExtension(); // and information about the Writer's Write() method
}
template <class VTKWRITER>
mitk::SurfaceVtkWriter<VTKWRITER>::~SurfaceVtkWriter()
{
}
template <class VTKWRITER>
void mitk::SurfaceVtkWriter<VTKWRITER>::SetDefaultExtension()
{
m_Extension = ".vtk";
}
template <class VTKWRITER>
void mitk::SurfaceVtkWriter<VTKWRITER>::ExecuteWrite(VtkWriterType *vtkWriter)
{
if (vtkWriter->Write() == 0 || vtkWriter->GetErrorCode() != 0)
{
itkExceptionMacro(<< "Error during surface writing: "
<< vtkErrorCode::GetStringFromErrorCode(vtkWriter->GetErrorCode()));
}
}
template <class VTKWRITER>
void mitk::SurfaceVtkWriter<VTKWRITER>::GenerateData()
{
if (m_FileName == "")
{
itkWarningMacro(<< "Sorry, filename has not been set!");
return;
}
mitk::Surface::Pointer input = const_cast<mitk::Surface *>(this->GetInput());
vtkSmartPointer<vtkTransformPolyDataFilter> transformPolyData = vtkSmartPointer<vtkTransformPolyDataFilter>::New();
vtkPolyData *polyData;
BaseGeometry *geometry;
unsigned int t, timesteps = input->GetTimeGeometry()->CountTimeSteps();
for (t = 0; t < timesteps; ++t)
{
// surfaces do not have to exist in all timeteps; therefor, only write valid surfaces
if (input->GetVtkPolyData(t) == nullptr)
continue;
std::ostringstream filename;
filename.imbue(::std::locale::classic());
geometry = input->GetGeometry(t);
if (timesteps > 1)
{
if (input->GetTimeGeometry()->IsValidTimeStep(t))
{
const TimeBounds timebounds = input->GetTimeGeometry()->GetTimeBounds(t);
filename << m_FileName.c_str() << "_S" << std::setprecision(0) << timebounds[0] << "_E" << std::setprecision(0)
<< timebounds[1] << "_T" << t << m_Extension;
}
else
{
itkWarningMacro(<< "Error on write: TimeGeometry invalid of surface " << filename.str() << ".");
filename << m_FileName.c_str() << "_T" << t << m_Extension;
}
m_VtkWriter->SetFileName(filename.str().c_str());
}
else
m_VtkWriter->SetFileName(m_FileName.c_str());
transformPolyData->SetInputData(input->GetVtkPolyData(t));
transformPolyData->SetTransform(geometry->GetVtkTransform());
transformPolyData->UpdateWholeExtent();
polyData = transformPolyData->GetOutput();
m_VtkWriter->SetInputData(polyData);
ExecuteWrite(m_VtkWriter);
}
m_MimeType = "application/MITK.Surface";
}
template <class VTKWRITER>
void mitk::SurfaceVtkWriter<VTKWRITER>::SetInput(mitk::Surface *surface)
{
this->ProcessObject::SetNthInput(0, surface);
}
template <class VTKWRITER>
const mitk::Surface *mitk::SurfaceVtkWriter<VTKWRITER>::GetInput()
{
if (this->GetNumberOfInputs() < 1)
{
return nullptr;
}
else
{
return static_cast<const Surface *>(this->ProcessObject::GetInput(0));
}
}
template <class VTKWRITER>
bool mitk::SurfaceVtkWriter<VTKWRITER>::CanWriteDataType(DataNode *input)
{
if (input)
{
BaseData *data = input->GetData();
if (data)
{
Surface::Pointer surface = dynamic_cast<Surface *>(data);
if (surface.IsNotNull())
{
SetDefaultExtension();
return true;
}
}
}
return false;
}
template <class VTKWRITER>
void mitk::SurfaceVtkWriter<VTKWRITER>::SetInput(DataNode *input)
{
if (input && CanWriteDataType(input))
SetInput(dynamic_cast<Surface *>(input->GetData()));
}
template <class VTKWRITER>
std::string mitk::SurfaceVtkWriter<VTKWRITER>::GetWritenMIMEType()
{
return m_MimeType;
}
template <class VTKWRITER>
std::string mitk::SurfaceVtkWriter<VTKWRITER>::GetFileExtension()
{
return m_Extension;
}
diff --git a/Modules/MapperExt/src/vtkMitkGPUVolumeRayCastMapper.cpp b/Modules/MapperExt/src/vtkMitkGPUVolumeRayCastMapper.cpp
index 7858b5a49d..d2db1547f4 100644
--- a/Modules/MapperExt/src/vtkMitkGPUVolumeRayCastMapper.cpp
+++ b/Modules/MapperExt/src/vtkMitkGPUVolumeRayCastMapper.cpp
@@ -1,619 +1,619 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
/*=========================================================================
Program: Visualization Toolkit
Module: $RCSfile: vtkMitkGPUVolumeRayCastMapper.cxx,v $
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkMitkGPUVolumeRayCastMapper.h"
#include "vtkCamera.h"
#include "vtkCellData.h"
#include "vtkCommand.h" // for VolumeMapperRender{Start|End|Progress}Event
#include "vtkDataArray.h"
#include "vtkGPUInfo.h"
#include "vtkGPUInfoList.h"
#include "vtkImageData.h"
#include "vtkImageResample.h"
#include "vtkMultiThreader.h"
#include "vtkPointData.h"
#include "vtkRenderWindow.h"
#include "vtkRenderer.h"
#include "vtkRendererCollection.h"
#include "vtkTimerLog.h"
#include "vtkVolume.h"
#include "vtkVolumeProperty.h"
#include <cassert>
vtkCxxSetObjectMacro(vtkMitkGPUVolumeRayCastMapper, MaskInput, vtkImageData);
vtkCxxSetObjectMacro(vtkMitkGPUVolumeRayCastMapper, TransformedInput, vtkImageData);
vtkMitkGPUVolumeRayCastMapper::vtkMitkGPUVolumeRayCastMapper()
{
this->AutoAdjustSampleDistances = 1;
this->ImageSampleDistance = 1.0;
this->MinimumImageSampleDistance = 1.0;
this->MaximumImageSampleDistance = 10.0;
this->SampleDistance = 1.0;
this->SmallVolumeRender = 0;
this->BigTimeToDraw = 0.0;
this->SmallTimeToDraw = 0.0;
this->FinalColorWindow = 1.0;
this->FinalColorLevel = 0.5;
this->GeneratingCanonicalView = 0;
this->CanonicalViewImageData = nullptr;
this->MaskInput = nullptr;
this->MaskBlendFactor = 1.0f;
this->AMRMode = 0;
this->ClippedCroppingRegionPlanes[0] = VTK_DOUBLE_MAX;
this->ClippedCroppingRegionPlanes[1] = VTK_DOUBLE_MIN;
this->ClippedCroppingRegionPlanes[2] = VTK_DOUBLE_MAX;
this->ClippedCroppingRegionPlanes[3] = VTK_DOUBLE_MIN;
this->ClippedCroppingRegionPlanes[4] = VTK_DOUBLE_MAX;
this->ClippedCroppingRegionPlanes[5] = VTK_DOUBLE_MIN;
this->MaxMemoryInBytes = 0;
vtkGPUInfoList *l = vtkGPUInfoList::New();
l->Probe();
if (l->GetNumberOfGPUs() > 0)
{
vtkGPUInfo *info = l->GetGPUInfo(0);
this->MaxMemoryInBytes = info->GetDedicatedVideoMemory();
if (this->MaxMemoryInBytes == 0)
{
this->MaxMemoryInBytes = info->GetDedicatedSystemMemory();
}
// we ignore info->GetSharedSystemMemory(); as this is very slow.
}
l->Delete();
if (this->MaxMemoryInBytes == 0) // use some default value: 128MB.
{
this->MaxMemoryInBytes = 128 * 1024 * 1024;
}
this->MaxMemoryFraction = 0.75;
this->ReportProgress = true;
this->TransformedInput = nullptr;
this->LastInput = nullptr;
}
// ----------------------------------------------------------------------------
vtkMitkGPUVolumeRayCastMapper::~vtkMitkGPUVolumeRayCastMapper()
{
this->SetMaskInput(nullptr);
this->SetTransformedInput(nullptr);
this->LastInput = nullptr;
}
// ----------------------------------------------------------------------------
// The render method that is called from the volume. If this is a canonical
// view render, a specialized version of this method will be called instead.
// Otherwise we will
// - Invoke a start event
// - Start timing
// - Check that everything is OK for rendering
// - Render
// - Stop the timer and record results
// - Invoke an end event
// ----------------------------------------------------------------------------
void vtkMitkGPUVolumeRayCastMapper::Render(vtkRenderer *ren, vtkVolume *vol)
{
// Catch renders that are happening due to a canonical view render and
// handle them separately.
if (this->GeneratingCanonicalView)
{
this->CanonicalViewRender(ren, vol);
return;
}
// Invoke a VolumeMapperRenderStartEvent
this->InvokeEvent(vtkCommand::VolumeMapperRenderStartEvent, nullptr);
// Start the timer to time the length of this render
vtkTimerLog *timer = vtkTimerLog::New();
timer->StartTimer();
// Make sure everything about this render is OK.
// This is where the input is updated.
if (this->ValidateRender(ren, vol))
{
// Everything is OK - so go ahead and really do the render
this->GPURender(ren, vol);
}
// Stop the timer
timer->StopTimer();
double t = timer->GetElapsedTime();
// cout << "Render Timer " << t << " seconds, " << 1.0/t << " frames per second" << endl;
this->TimeToDraw = t;
timer->Delete();
if (vol->GetAllocatedRenderTime() < 1.0)
{
this->SmallTimeToDraw = t;
}
else
{
this->BigTimeToDraw = t;
}
// Invoke a VolumeMapperRenderEndEvent
this->InvokeEvent(vtkCommand::VolumeMapperRenderEndEvent, nullptr);
}
// ----------------------------------------------------------------------------
// Special version for rendering a canonical view - we don't do things like
// invoke start or end events, and we don't capture the render time.
// ----------------------------------------------------------------------------
void vtkMitkGPUVolumeRayCastMapper::CanonicalViewRender(vtkRenderer *ren, vtkVolume *vol)
{
// Make sure everything about this render is OK
if (this->ValidateRender(ren, vol))
{
// Everything is OK - so go ahead and really do the render
this->GPURender(ren, vol);
}
}
// ----------------------------------------------------------------------------
// This method us used by the render method to validate everything before
// attempting to render. This method returns 0 if something is not right -
// such as missing input, a null renderer or a null volume, no scalars, etc.
// In some cases it will produce a vtkErrorMacro message, and in others
// (for example, in the case of cropping planes that define a region with
// a volume or 0 or less) it will fail silently. If everything is OK, it will
// return with a value of 1.
// ----------------------------------------------------------------------------
int vtkMitkGPUVolumeRayCastMapper::ValidateRender(vtkRenderer *ren, vtkVolume *vol)
{
// Check that we have everything we need to render.
int goodSoFar = 1;
// Check for a renderer - we MUST have one
if (!ren)
{
goodSoFar = 0;
vtkErrorMacro("Renderer cannot be null.");
}
// Check for the volume - we MUST have one
if (goodSoFar && !vol)
{
goodSoFar = 0;
vtkErrorMacro("Volume cannot be null.");
}
// Don't need to check if we have a volume property
// since the volume will create one if we don't. Also
// don't need to check for the scalar opacity function
// or the RGB transfer function since the property will
// create them if they do not yet exist.
// However we must currently check that the number of
// color channels is 3
// TODO: lift this restriction - should work with
// gray functions as well. Right now turning off test
// because otherwise 4 component rendering isn't working.
// Will revisit.
if (goodSoFar && vol->GetProperty()->GetColorChannels() != 3)
{
// goodSoFar = 0;
// vtkErrorMacro("Must have a color transfer function.");
}
// Check the cropping planes. If they are invalid, just silently
// fail. This will happen when an interactive widget is dragged
// such that it defines 0 or negative volume - this can happen
// and should just not render the volume.
// Check the cropping planes
if (goodSoFar && this->Cropping && (this->CroppingRegionPlanes[0] >= this->CroppingRegionPlanes[1] ||
this->CroppingRegionPlanes[2] >= this->CroppingRegionPlanes[3] ||
this->CroppingRegionPlanes[4] >= this->CroppingRegionPlanes[5]))
{
// No error message here - we want to be silent
goodSoFar = 0;
}
// Check that we have input data
- vtkImageData *input = this->GetInput();
+ auto *input = dynamic_cast<vtkImageData*>(this->GetInput());
// If we have a timestamp change or data change then create a new clone.
if (input != this->LastInput || input->GetMTime() > this->TransformedInput->GetMTime())
{
this->LastInput = input;
vtkImageData *clone;
if (!this->TransformedInput)
{
clone = vtkImageData::New();
this->SetTransformedInput(clone);
clone->Delete();
}
else
{
clone = this->TransformedInput;
}
clone->ShallowCopy(input);
// @TODO: This is the workaround to deal with GPUVolumeRayCastMapper
// not able to handle extents starting from non zero values.
// There is not a easy fix in the GPU volume ray cast mapper hence
// this fix has been introduced.
// Get the current extents.
int extents[6], real_extents[6];
clone->GetExtent(extents);
clone->GetExtent(real_extents);
// Get the current origin and spacing.
double origin[3], spacing[3];
clone->GetOrigin(origin);
clone->GetSpacing(spacing);
for (int cc = 0; cc < 3; cc++)
{
// Transform the origin and the extents.
origin[cc] = origin[cc] + extents[2 * cc] * spacing[cc];
extents[2 * cc + 1] -= extents[2 * cc];
extents[2 * cc] -= extents[2 * cc];
}
clone->SetOrigin(origin);
clone->SetExtent(extents);
}
if (goodSoFar && !this->TransformedInput)
{
vtkErrorMacro("Input is nullptr but is required");
goodSoFar = 0;
}
// Update the date then make sure we have scalars. Note
// that we must have point or cell scalars because field
// scalars are not supported.
vtkDataArray *scalars = nullptr;
if (goodSoFar)
{
// Here is where we update the input
// this->TransformedInput->UpdateInformation(); //VTK6_TODO
// this->TransformedInput->SetUpdateExtentToWholeExtent();
// this->TransformedInput->Update();
// Now make sure we can find scalars
scalars = this->GetScalars(
this->TransformedInput, this->ScalarMode, this->ArrayAccessMode, this->ArrayId, this->ArrayName, this->CellFlag);
// We couldn't find scalars
if (!scalars)
{
vtkErrorMacro("No scalars found on input.");
goodSoFar = 0;
}
// Even if we found scalars, if they are field data scalars that isn't good
else if (this->CellFlag == 2)
{
vtkErrorMacro("Only point or cell scalar support - found field scalars instead.");
goodSoFar = 0;
}
}
// Make sure the scalar type is actually supported. This mappers supports
// almost all standard scalar types.
if (goodSoFar)
{
switch (scalars->GetDataType())
{
case VTK_CHAR:
vtkErrorMacro(<< "scalar of type VTK_CHAR is not supported "
<< "because this type is platform dependent. "
<< "Use VTK_SIGNED_CHAR or VTK_UNSIGNED_CHAR instead.");
goodSoFar = 0;
break;
case VTK_BIT:
vtkErrorMacro("scalar of type VTK_BIT is not supported by this mapper.");
goodSoFar = 0;
break;
case VTK_ID_TYPE:
vtkErrorMacro("scalar of type VTK_ID_TYPE is not supported by this mapper.");
goodSoFar = 0;
break;
case VTK_STRING:
vtkErrorMacro("scalar of type VTK_STRING is not supported by this mapper.");
goodSoFar = 0;
break;
default:
// Don't need to do anything here
break;
}
}
// Check on the blending type - we support composite and min / max intensity
if (goodSoFar)
{
if (this->BlendMode != vtkVolumeMapper::COMPOSITE_BLEND &&
this->BlendMode != vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND &&
this->BlendMode != vtkVolumeMapper::MINIMUM_INTENSITY_BLEND)
{
goodSoFar = 0;
vtkErrorMacro(<< "Selected blend mode not supported. "
<< "Only Composite and MIP and MinIP modes "
<< "are supported by the current implementation.");
}
}
// This mapper supports 1 component data, or 4 component if it is not independent
// component (i.e. the four components define RGBA)
int numberOfComponents = 0;
if (goodSoFar)
{
numberOfComponents = scalars->GetNumberOfComponents();
if (!(numberOfComponents == 1 || (numberOfComponents == 4 && vol->GetProperty()->GetIndependentComponents() == 0)))
{
goodSoFar = 0;
vtkErrorMacro(<< "Only one component scalars, or four "
<< "component with non-independent components, "
<< "are supported by this mapper.");
}
}
// If this is four component data, then it better be unsigned char (RGBA).
if (goodSoFar && numberOfComponents == 4 && scalars->GetDataType() != VTK_UNSIGNED_CHAR)
{
goodSoFar = 0;
vtkErrorMacro("Only unsigned char is supported for 4-component scalars!");
}
// return our status
return goodSoFar;
}
// ----------------------------------------------------------------------------
// Description:
// Called by the AMR Volume Mapper.
// Set the flag that tells if the scalars are on point data (0) or
// cell data (1).
void vtkMitkGPUVolumeRayCastMapper::SetCellFlag(int cellFlag)
{
this->CellFlag = cellFlag;
}
// ----------------------------------------------------------------------------
void vtkMitkGPUVolumeRayCastMapper::CreateCanonicalView(vtkRenderer *ren,
vtkVolume *volume,
vtkImageData *image,
int vtkNotUsed(blend_mode),
double viewDirection[3],
double viewUp[3])
{
this->GeneratingCanonicalView = 1;
int oldSwap = ren->GetRenderWindow()->GetSwapBuffers();
ren->GetRenderWindow()->SwapBuffersOff();
int dim[3];
image->GetDimensions(dim);
int *size = ren->GetRenderWindow()->GetSize();
vtkImageData *bigImage = vtkImageData::New();
bigImage->SetDimensions(size[0], size[1], 1);
bigImage->AllocateScalars(VTK_UNSIGNED_CHAR, 3);
this->CanonicalViewImageData = bigImage;
double scale[2];
scale[0] = dim[0] / static_cast<double>(size[0]);
scale[1] = dim[1] / static_cast<double>(size[1]);
// Save the visibility flags of the renderers and set all to false except
// for the ren.
vtkRendererCollection *renderers = ren->GetRenderWindow()->GetRenderers();
int numberOfRenderers = renderers->GetNumberOfItems();
auto rendererVisibilities = new bool[numberOfRenderers];
renderers->InitTraversal();
int i = 0;
while (i < numberOfRenderers)
{
vtkRenderer *r = renderers->GetNextItem();
rendererVisibilities[i] = r->GetDraw() == 1;
if (r != ren)
{
r->SetDraw(false);
}
++i;
}
// Save the visibility flags of the props and set all to false except
// for the volume.
vtkPropCollection *props = ren->GetViewProps();
int numberOfProps = props->GetNumberOfItems();
auto propVisibilities = new bool[numberOfProps];
props->InitTraversal();
i = 0;
while (i < numberOfProps)
{
vtkProp *p = props->GetNextProp();
propVisibilities[i] = p->GetVisibility() == 1;
if (p != volume)
{
p->SetVisibility(false);
}
++i;
}
vtkCamera *savedCamera = ren->GetActiveCamera();
savedCamera->Modified();
vtkCamera *canonicalViewCamera = vtkCamera::New();
// Code from vtkFixedPointVolumeRayCastMapper:
double *center = volume->GetCenter();
double bounds[6];
volume->GetBounds(bounds);
double d =
sqrt((bounds[1] - bounds[0]) * (bounds[1] - bounds[0]) + (bounds[3] - bounds[2]) * (bounds[3] - bounds[2]) +
(bounds[5] - bounds[4]) * (bounds[5] - bounds[4]));
// For now use x distance - need to change this
d = bounds[1] - bounds[0];
// Set up the camera in parallel
canonicalViewCamera->SetFocalPoint(center);
canonicalViewCamera->ParallelProjectionOn();
canonicalViewCamera->SetPosition(
center[0] - d * viewDirection[0], center[1] - d * viewDirection[1], center[2] - d * viewDirection[2]);
canonicalViewCamera->SetViewUp(viewUp);
canonicalViewCamera->SetParallelScale(d / 2);
ren->SetActiveCamera(canonicalViewCamera);
ren->GetRenderWindow()->Render();
ren->SetActiveCamera(savedCamera);
canonicalViewCamera->Delete();
// Shrink to image to the desired size
vtkImageResample *resample = vtkImageResample::New();
resample->SetInputData(bigImage);
resample->SetAxisMagnificationFactor(0, scale[0]);
resample->SetAxisMagnificationFactor(1, scale[1]);
resample->SetAxisMagnificationFactor(2, 1);
resample->UpdateWholeExtent();
// Copy the pixels over
image->DeepCopy(resample->GetOutput());
bigImage->Delete();
resample->Delete();
// Restore the visibility flags of the props
props->InitTraversal();
i = 0;
while (i < numberOfProps)
{
vtkProp *p = props->GetNextProp();
p->SetVisibility(propVisibilities[i]);
++i;
}
delete[] propVisibilities;
// Restore the visibility flags of the renderers
renderers->InitTraversal();
i = 0;
while (i < numberOfRenderers)
{
vtkRenderer *r = renderers->GetNextItem();
r->SetDraw(rendererVisibilities[i]);
++i;
}
delete[] rendererVisibilities;
ren->GetRenderWindow()->SetSwapBuffers(oldSwap);
this->CanonicalViewImageData = nullptr;
this->GeneratingCanonicalView = 0;
}
// ----------------------------------------------------------------------------
// Print method for vtkMitkGPUVolumeRayCastMapper
void vtkMitkGPUVolumeRayCastMapper::PrintSelf(ostream &os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
os << indent << "AutoAdjustSampleDistances: " << this->AutoAdjustSampleDistances << endl;
os << indent << "MinimumImageSampleDistance: " << this->MinimumImageSampleDistance << endl;
os << indent << "MaximumImageSampleDistance: " << this->MaximumImageSampleDistance << endl;
os << indent << "ImageSampleDistance: " << this->ImageSampleDistance << endl;
os << indent << "SampleDistance: " << this->SampleDistance << endl;
os << indent << "FinalColorWindow: " << this->FinalColorWindow << endl;
os << indent << "FinalColorLevel: " << this->FinalColorLevel << endl;
os << indent << "MaskInput: " << this->MaskInput << endl;
os << indent << "MaskBlendFactor: " << this->MaskBlendFactor << endl;
os << indent << "MaxMemoryInBytes: " << this->MaxMemoryInBytes << endl;
os << indent << "MaxMemoryFraction: " << this->MaxMemoryFraction << endl;
os << indent << "ReportProgress: " << this->ReportProgress << endl;
}
// ----------------------------------------------------------------------------
// Description:
// Compute the cropping planes clipped by the bounds of the volume.
// The result is put into this->ClippedCroppingRegionPlanes.
// NOTE: IT WILL BE MOVED UP TO vtkVolumeMapper after bullet proof usage
// in this mapper. Other subclasses will use the ClippedCroppingRegionsPlanes
// members instead of CroppingRegionPlanes.
// \pre volume_exists: this->GetInput()!=0
// \pre valid_cropping: this->Cropping &&
// this->CroppingRegionPlanes[0]<this->CroppingRegionPlanes[1] &&
// this->CroppingRegionPlanes[2]<this->CroppingRegionPlanes[3] &&
// this->CroppingRegionPlanes[4]<this->CroppingRegionPlanes[5])
void vtkMitkGPUVolumeRayCastMapper::ClipCroppingRegionPlanes()
{
assert("pre: volume_exists" && this->GetInput() != nullptr);
assert("pre: valid_cropping" && this->Cropping && this->CroppingRegionPlanes[0] < this->CroppingRegionPlanes[1] &&
this->CroppingRegionPlanes[2] < this->CroppingRegionPlanes[3] &&
this->CroppingRegionPlanes[4] < this->CroppingRegionPlanes[5]);
// vtkVolumeMapper::Render() will have something like:
// if(this->Cropping && (this->CroppingRegionPlanes[0]>=this->CroppingRegionPlanes[1] ||
// this->CroppingRegionPlanes[2]>=this->CroppingRegionPlanes[3] ||
// this->CroppingRegionPlanes[4]>=this->CroppingRegionPlanes[5]))
// {
// // silentely stop because the cropping is not valid.
// return;
// }
double volBounds[6];
this->GetInput()->GetBounds(volBounds);
int i = 0;
while (i < 6)
{
// max of the mins
if (this->CroppingRegionPlanes[i] < volBounds[i])
{
this->ClippedCroppingRegionPlanes[i] = volBounds[i];
}
else
{
this->ClippedCroppingRegionPlanes[i] = this->CroppingRegionPlanes[i];
}
++i;
// min of the maxs
if (this->CroppingRegionPlanes[i] > volBounds[i])
{
this->ClippedCroppingRegionPlanes[i] = volBounds[i];
}
else
{
this->ClippedCroppingRegionPlanes[i] = this->CroppingRegionPlanes[i];
}
++i;
}
}
diff --git a/Modules/MatchPointRegistration/include/itkStitchImageFilter.h b/Modules/MatchPointRegistration/include/itkStitchImageFilter.h
index 5d5edce5d0..c72836aeca 100644
--- a/Modules/MatchPointRegistration/include/itkStitchImageFilter.h
+++ b/Modules/MatchPointRegistration/include/itkStitchImageFilter.h
@@ -1,330 +1,330 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef itkStitchImageFilter_h
#define itkStitchImageFilter_h
#include "itkFixedArray.h"
#include "itkTransform.h"
#include "itkImageRegionIterator.h"
#include "itkImageToImageFilter.h"
#include "itkLinearInterpolateImageFunction.h"
#include "itkSize.h"
#include "itkDefaultConvertPixelTraits.h"
#include "itkDataObjectDecorator.h"
namespace itk
{
enum class StitchStrategy
{
Mean = 0, //use the mean value of all inputs that can provide a pixel vaule
BorderDistance = 1 //use the value that is largest minimal distance to its image borders (use e.g. if vaules tend to be not reliable at borders)
};
std::ostream& operator<< (std::ostream& os, const itk::StitchStrategy& strategy)
{
if (itk::StitchStrategy::Mean == strategy)
os << "Mean";
else if (itk::StitchStrategy::BorderDistance == strategy)
os << "BorderDistance";
else
os << "unkown";
return os;
};
/** \class StitchImageFilter
* \brief ITK filter that resamples/stitches multiple images into a given reference geometry.
*
* StitchImageFilter is similar to itk's ResampleImageFilter, but in difference to the last
* mentioned StitchImageFilter is able to resample multiple input images at once (with a transform
* for each input image). If multiple input images cover the output region the behavior depends on
* the StitchStragy:
* - Mean: a weighted sum of all voxels mapped input pixel values will be calculated.
* - BorderDistance: the voxels will be choosen that have the largest minimal distance to its own image borders.
*
* All other behaviors are similar to itk::ResampleImageFilter. See the filter's description for
* more details.
*/
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType = double,
typename TTransformPrecisionType = TInterpolatorPrecisionType>
class StitchImageFilter :
public ImageToImageFilter< TInputImage, TOutputImage >
{
public:
/** Standard class typedefs. */
typedef StitchImageFilter Self;
typedef ImageToImageFilter< TInputImage, TOutputImage > Superclass;
typedef SmartPointer< Self > Pointer;
typedef SmartPointer< const Self > ConstPointer;
typedef TInputImage InputImageType;
typedef TOutputImage OutputImageType;
typedef typename InputImageType::Pointer InputImagePointer;
typedef typename InputImageType::ConstPointer InputImageConstPointer;
typedef typename OutputImageType::Pointer OutputImagePointer;
typedef typename InputImageType::RegionType InputImageRegionType;
/** Method for creation through the object factory. */
itkNewMacro(Self);
/** Run-time type information (and related methods). */
itkTypeMacro(StitchImageFilter, ImageToImageFilter);
/** Number of dimensions. */
itkStaticConstMacro(ImageDimension, unsigned int,
TOutputImage::ImageDimension);
itkStaticConstMacro(InputImageDimension, unsigned int,
TInputImage::ImageDimension);
/** base type for images of the current ImageDimension */
typedef ImageBase< itkGetStaticConstMacro(ImageDimension) > ImageBaseType;
/**
* Transform typedef.
*/
typedef Transform< TTransformPrecisionType,
itkGetStaticConstMacro(ImageDimension),
itkGetStaticConstMacro(ImageDimension) > TransformType;
typedef typename TransformType::ConstPointer TransformPointerType;
typedef DataObjectDecorator<TransformType> DecoratedTransformType;
typedef typename DecoratedTransformType::Pointer DecoratedTransformPointer;
/** Interpolator typedef. */
typedef InterpolateImageFunction< InputImageType,
TInterpolatorPrecisionType > InterpolatorType;
typedef typename InterpolatorType::Pointer InterpolatorPointerType;
typedef typename InterpolatorType::OutputType InterpolatorOutputType;
typedef DefaultConvertPixelTraits< InterpolatorOutputType > InterpolatorConvertType;
typedef typename InterpolatorConvertType::ComponentType ComponentType;
typedef LinearInterpolateImageFunction< InputImageType,
TInterpolatorPrecisionType > LinearInterpolatorType;
typedef typename LinearInterpolatorType::Pointer
LinearInterpolatorPointerType;
/** Image size typedef. */
typedef Size< itkGetStaticConstMacro(ImageDimension) > SizeType;
/** Image index typedef. */
typedef typename TOutputImage::IndexType IndexType;
/** Image point typedef. */
typedef typename InterpolatorType::PointType PointType;
//typedef typename TOutputImage::PointType PointType;
/** Image pixel value typedef. */
typedef typename TOutputImage::PixelType PixelType;
typedef typename TInputImage::PixelType InputPixelType;
typedef DefaultConvertPixelTraits<PixelType> PixelConvertType;
typedef typename PixelConvertType::ComponentType PixelComponentType;
/** Input pixel continuous index typdef */
typedef ContinuousIndex< TTransformPrecisionType, ImageDimension >
ContinuousInputIndexType;
/** Typedef to describe the output image region type. */
typedef typename TOutputImage::RegionType OutputImageRegionType;
/** Image spacing,origin and direction typedef */
typedef typename TOutputImage::SpacingType SpacingType;
typedef typename TOutputImage::PointType OriginPointType;
typedef typename TOutputImage::DirectionType DirectionType;
using Superclass::GetInput;
/** Typedef the reference image type to be the ImageBase of the OutputImageType */
typedef ImageBase<ImageDimension> ReferenceImageBaseType;
using Superclass::SetInput;
void SetInput(const InputImageType* image) override;
void SetInput(unsigned int index, const InputImageType* image) override;
/** Convinience methods that allows setting of input image and its transform in
one call.*/
virtual void SetInput(unsigned int index, const InputImageType* image, const TransformType* transform);
virtual void SetInput(unsigned int index, const InputImageType* image, const TransformType* transform, InterpolatorType* interpolator);
const TransformType* GetTransform(unsigned int index) const;
const InterpolatorType* GetInterpolator(unsigned int index) const;
/** Get/Set the size of the output image. */
itkSetMacro(Size, SizeType);
itkGetConstReferenceMacro(Size, SizeType);
/** Get/Set the pixel value when a transformed pixel is outside of the
* image. The default default pixel value is 0. */
itkSetMacro(DefaultPixelValue, PixelType);
itkGetConstReferenceMacro(DefaultPixelValue, PixelType);
/** Set the output image spacing. */
itkSetMacro(OutputSpacing, SpacingType);
virtual void SetOutputSpacing(const double *values);
/** Get the output image spacing. */
itkGetConstReferenceMacro(OutputSpacing, SpacingType);
/** Set the output image origin. */
itkSetMacro(OutputOrigin, OriginPointType);
virtual void SetOutputOrigin(const double *values);
/** Get the output image origin. */
itkGetConstReferenceMacro(OutputOrigin, OriginPointType);
/** Set the output direciton cosine matrix. */
itkSetMacro(OutputDirection, DirectionType);
itkGetConstReferenceMacro(OutputDirection, DirectionType);
/** Helper method to set the output parameters based on this image. */
void SetOutputParametersFromImage(const ImageBaseType *image);
/** Set the start index of the output largest possible region.
* The default is an index of all zeros. */
itkSetMacro(OutputStartIndex, IndexType);
/** Get the start index of the output largest possible region. */
itkGetConstReferenceMacro(OutputStartIndex, IndexType);
/** Set a reference image to use to define the output information.
* By default, output information is specificed through the
* SetOutputSpacing, Origin, and Direction methods. Alternatively,
* this method can be used to specify an image from which to
* copy the information. UseReferenceImageOn must be set to utilize the
* reference image. */
itkSetInputMacro(ReferenceImage, ReferenceImageBaseType);
/** Get the reference image that is defining the output information. */
itkGetInputMacro(ReferenceImage, ReferenceImageBaseType);
/** Turn on/off whether a specified reference image should be used to define
* the output information. */
itkSetMacro(UseReferenceImage, bool);
itkBooleanMacro(UseReferenceImage);
itkGetConstMacro(UseReferenceImage, bool);
itkSetMacro(StitchStrategy, StitchStrategy);
itkGetConstMacro(StitchStrategy, StitchStrategy);
/** StitchImageFilter produces an image which is a different size
* than its input. As such, it needs to provide an implementation
* for GenerateOutputInformation() in order to inform the pipeline
* execution model. The original documentation of this method is
* below. \sa ProcessObject::GenerateOutputInformaton() */
virtual void GenerateOutputInformation() ITK_OVERRIDE;
/** StitchImageFilter needs a different input requested region than
* the output requested region. As such, StitchImageFilter needs
* to provide an implementation for GenerateInputRequestedRegion()
* in order to inform the pipeline execution model.
* \sa ProcessObject::GenerateInputRequestedRegion() */
virtual void GenerateInputRequestedRegion() ITK_OVERRIDE;
/** Set up state of filter before multi-threading.
* InterpolatorType::SetInputImage is not thread-safe and hence
* has to be set up before ThreadedGenerateData */
virtual void BeforeThreadedGenerateData() ITK_OVERRIDE;
/** Set the state of the filter after multi-threading. */
virtual void AfterThreadedGenerateData() ITK_OVERRIDE;
/** Compute the Modified Time based on the changed components. */
ModifiedTimeType GetMTime(void) const ITK_OVERRIDE;
#ifdef ITK_USE_CONCEPT_CHECKING
// Begin concept checking
itkConceptMacro( OutputHasNumericTraitsCheck,
( Concept::HasNumericTraits< PixelComponentType > ) );
// End concept checking
#endif
protected:
StitchImageFilter();
~StitchImageFilter() ITK_OVERRIDE {}
void PrintSelf(std::ostream & os, Indent indent) const ITK_OVERRIDE;
/** Override VeriyInputInformation() since this filter's inputs do
* not need to occoupy the same physical space.
*
* \sa ProcessObject::VerifyInputInformation
*/
- virtual void VerifyInputInformation() ITK_OVERRIDE { }
+ virtual void VerifyInputInformation() const ITK_OVERRIDE { }
/** StitchImageFilter can be implemented as a multithreaded filter.
* Therefore, this implementation provides a ThreadedGenerateData()
* routine which is called for each processing thread. The output
* image data is allocated automatically by the superclass prior
* to calling ThreadedGenerateData().
* ThreadedGenerateData can only write to the portion of the output image
* specified by the parameter "outputRegionForThread"
* \sa ImageToImageFilter::ThreadedGenerateData(),
* ImageToImageFilter::GenerateData() */
virtual void ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread,
ThreadIdType threadId) ITK_OVERRIDE;
/** Cast pixel from interpolator output to PixelType. */
virtual PixelType CastPixelWithBoundsChecking( const InterpolatorOutputType value,
const ComponentType minComponent,
const ComponentType maxComponent) const;
void SetTransform(unsigned int index, const TransformType* transform);
/** Helper that ensures that a transform is specified for every input image.
If a input image has no specified transforms, an identity transform will
be created and set as default.*/
void EnsureTransforms();
/** Helper that ensures that an interpolator is specified for every input image.
If a input image has no specified interpolator, a linear interpolator will
be created and set as default.*/
void EnsureInterpolators();
static std::string GetTransformInputName(unsigned int index);
private:
ITK_DISALLOW_COPY_AND_ASSIGN(StitchImageFilter);
typedef std::vector<const InputImageType*> InputImageVectorType;
typedef std::map<const InputImageType*, typename TransformType::ConstPointer> TransformMapType;
typedef std::map<const InputImageType*, InterpolatorPointerType> InterpolatorMapType;
InputImageVectorType GetInputs();
TransformMapType GetTransforms();
InterpolatorMapType m_Interpolators; // Image function for
// interpolation
PixelType m_DefaultPixelValue; // default pixel value
// if the point is
// outside the image
SizeType m_Size; // Size of the output image
SpacingType m_OutputSpacing; // output image spacing
OriginPointType m_OutputOrigin; // output image origin
DirectionType m_OutputDirection; // output image direction cosines
IndexType m_OutputStartIndex; // output image start index
bool m_UseReferenceImage;
StitchStrategy m_StitchStrategy;
};
} // end namespace itk
#ifndef ITK_MANUAL_INSTANTIATION
#include "itkStitchImageFilter.tpp"
#endif
#endif
diff --git a/Modules/MatchPointRegistration/include/itkStitchImageFilter.tpp b/Modules/MatchPointRegistration/include/itkStitchImageFilter.tpp
index 5e8e7529c9..2b535e174f 100644
--- a/Modules/MatchPointRegistration/include/itkStitchImageFilter.tpp
+++ b/Modules/MatchPointRegistration/include/itkStitchImageFilter.tpp
@@ -1,639 +1,639 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef itkStitchImageFilter_hxx
#define itkStitchImageFilter_hxx
#include "itkStitchImageFilter.h"
#include "itkObjectFactory.h"
#include "itkIdentityTransform.h"
#include "itkProgressReporter.h"
#include "itkImageRegionIteratorWithIndex.h"
#include "itkImageScanlineIterator.h"
#include "itkSpecialCoordinatesImage.h"
#include "itkDefaultConvertPixelTraits.h"
#include "itkSimpleDataObjectDecorator.h"
#include <numeric>
namespace itk
{
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::StitchImageFilter() :
m_OutputSpacing( 1.0 ),
m_OutputOrigin( 0.0 ),
m_UseReferenceImage( false ),
m_StitchStrategy(StitchStrategy::Mean)
{
+ this->DynamicMultiThreadingOff();
m_Size.Fill( 0 );
m_OutputStartIndex.Fill( 0 );
m_OutputDirection.SetIdentity();
// Pipeline input configuration
// implicit input index set:
// #1 "ReferenceImage" optional
Self::AddOptionalInputName("ReferenceImage");
m_DefaultPixelValue
= NumericTraits<PixelType>::ZeroValue( m_DefaultPixelValue );
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
void
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::SetInput(const InputImageType* image)
{
this->SetInput(0, image, itk::IdentityTransform< TTransformPrecisionType, ImageDimension>::New().GetPointer(), LinearInterpolatorType::New().GetPointer());
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
void
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::SetInput(unsigned int index, const InputImageType* image)
{
this->SetInput(index, image, itk::IdentityTransform< TTransformPrecisionType, ImageDimension>::New().GetPointer(), LinearInterpolatorType::New().GetPointer());
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
void
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::SetInput(unsigned int index, const InputImageType* image, const TransformType* transform)
{
this->SetInput(index, image, transform, LinearInterpolatorType::New().GetPointer());
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
void
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::SetInput(unsigned int index, const InputImageType* image, const TransformType* transform, InterpolatorType* interpolator)
{
Superclass::SetInput(index, image);
m_Interpolators[image] = interpolator;
this->SetTransform(index, transform);
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
void
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::SetTransform(unsigned int index, const TransformType* transform)
{
const auto transformName = this->GetTransformInputName(index);
typedef SimpleDataObjectDecorator< TransformPointerType > DecoratorType;
const DecoratorType* oldInput = itkDynamicCastInDebugMode< const DecoratorType* >(this->ProcessObject::GetInput(transformName));
if (!oldInput || oldInput->Get() != transform)
{
typename DecoratorType::Pointer newInput = DecoratorType::New();
// Process object is not const-correct so the const_cast is required here
newInput->Set(const_cast<TransformType*>(transform));
this->ProcessObject::SetInput(transformName, newInput);
}
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
const typename StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >::TransformType*
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::GetTransform(unsigned int index) const
{
typedef SimpleDataObjectDecorator< TransformPointerType > DecoratorType;
const DecoratorType* input = itkDynamicCastInDebugMode< const DecoratorType* >(this->ProcessObject::GetInput(this->GetTransformInputName(index)));
if (nullptr != input)
{
return input->Get();
}
return nullptr;
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
const typename StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >::InterpolatorType*
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::GetInterpolator(unsigned int index) const
{
auto input = this->GetInput(index);
if (m_Interpolators.find(input) != std::end(m_Interpolators))
{
return m_Interpolators[input];
}
return nullptr;
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
void
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::SetOutputSpacing(const double *spacing)
{
SpacingType s;
for(unsigned int i = 0; i < TOutputImage::ImageDimension; ++i)
{
s[i] = static_cast< typename SpacingType::ValueType >(spacing[i]);
}
this->SetOutputSpacing(s);
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
void
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::SetOutputOrigin(const double *origin)
{
OriginPointType p(origin);
this->SetOutputOrigin(p);
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
void
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::SetOutputParametersFromImage(const ImageBaseType *image)
{
this->SetOutputOrigin ( image->GetOrigin() );
this->SetOutputSpacing ( image->GetSpacing() );
this->SetOutputDirection ( image->GetDirection() );
this->SetOutputStartIndex ( image->GetLargestPossibleRegion().GetIndex() );
this->SetSize ( image->GetLargestPossibleRegion().GetSize() );
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
void
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::BeforeThreadedGenerateData()
{
this->EnsureInterpolators();
this->EnsureTransforms();
for (const auto& interpolator : m_Interpolators)
{
interpolator.second->SetInputImage(interpolator.first);
}
unsigned int nComponents
= DefaultConvertPixelTraits<PixelType>::GetNumberOfComponents(
m_DefaultPixelValue );
if (nComponents == 0)
{
PixelComponentType zeroComponent
= NumericTraits<PixelComponentType>::ZeroValue();
nComponents = this->GetInput()->GetNumberOfComponentsPerPixel();
NumericTraits<PixelType>::SetLength(m_DefaultPixelValue, nComponents );
for (unsigned int n=0; n<nComponents; n++)
{
PixelConvertType::SetNthComponent( n, m_DefaultPixelValue,
zeroComponent );
}
}
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
void
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::AfterThreadedGenerateData()
{
// Disconnect input image from the interpolator
for (auto& interpolator : m_Interpolators)
{
interpolator.second->SetInputImage(ITK_NULLPTR);
}
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
void
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread,
ThreadIdType threadId)
{
if( outputRegionForThread.GetNumberOfPixels() == 0 )
{
return;
}
// Get the output pointers
OutputImageType* outputPtr = this->GetOutput();
// Get this input pointers
InputImageVectorType inputs = this->GetInputs();
TransformMapType transforms = this->GetTransforms();
std::map<const InputImageType*, typename InputImageType::IndexType> lowerIndices;
std::map<const InputImageType*, typename InputImageType::IndexType> upperIndices;
for (const auto& input : inputs)
{
const auto largestRegion = input->GetLargestPossibleRegion();
lowerIndices[input] = largestRegion.GetIndex();
upperIndices[input] = largestRegion.GetUpperIndex();
}
// Create an iterator that will walk the output region for this thread.
typedef ImageRegionIteratorWithIndex< OutputImageType > OutputIterator;
OutputIterator outIt(outputPtr, outputRegionForThread);
// Define a few indices that will be used to translate from an input pixel
// to an output pixel
PointType outputPoint; // Coordinates of current output pixel
PointType inputPoint; // Coordinates of current input pixel
ContinuousInputIndexType inputIndex;
// Support for progress methods/callbacks
ProgressReporter progress(this,
threadId,
outputRegionForThread.GetNumberOfPixels());
// Min/max values of the output pixel type AND these values
// represented as the output type of the interpolator
const PixelComponentType minValue = NumericTraits< PixelComponentType >::NonpositiveMin();
const PixelComponentType maxValue = NumericTraits< PixelComponentType >::max();
typedef typename InterpolatorType::OutputType OutputType;
const ComponentType minOutputValue = static_cast<ComponentType>(minValue);
const ComponentType maxOutputValue = static_cast<ComponentType>(maxValue);
// Walk the output region
outIt.GoToBegin();
while (!outIt.IsAtEnd())
{
// Determine the index of the current output pixel
outputPtr->TransformIndexToPhysicalPoint(outIt.GetIndex(), outputPoint);
std::vector<PixelType> pixvals;
std::vector<double> pixDistance;
for (const auto& input : inputs)
{
// Compute corresponding input pixel position
inputPoint = transforms[input]->TransformPoint(outputPoint);
const bool isInsideInput = input->TransformPhysicalPointToContinuousIndex(inputPoint, inputIndex);
// Evaluate input at right position and copy to the output
if (m_Interpolators[input]->IsInsideBuffer(inputIndex) && isInsideInput)
{
OutputType value = m_Interpolators[input]->EvaluateAtContinuousIndex(inputIndex);
pixvals.emplace_back(this->CastPixelWithBoundsChecking(value, minOutputValue, maxOutputValue));
- ContinuousInputIndexType indexDistance;
const auto spacing = input->GetSpacing();
double minBorderDistance = std::numeric_limits<double>::max();
for (unsigned int i = 0; i < ImageDimension; ++i)
{
minBorderDistance = std::min(minBorderDistance, std::min(std::abs(lowerIndices[input][i] - inputIndex[i]) * spacing[i], std::abs(upperIndices[input][i] - inputIndex[i]) * spacing[i]));
}
pixDistance.emplace_back(minBorderDistance);
}
}
if (!pixvals.empty())
{ //at least one input provided a value
if (StitchStrategy::Mean == m_StitchStrategy)
{
double sum = std::accumulate(pixvals.begin(), pixvals.end(), 0.0);
outIt.Set(sum / pixvals.size());
}
else
{
auto finding = std::max_element(pixDistance.begin(), pixDistance.end());
outIt.Set(pixvals[std::distance(pixDistance.begin(), finding)]);
}
}
else
{
outIt.Set(m_DefaultPixelValue); // default background value
}
progress.CompletedPixel();
++outIt;
}
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
typename StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::PixelType
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::CastPixelWithBoundsChecking(const InterpolatorOutputType value,
const ComponentType minComponent,
const ComponentType maxComponent ) const
{
const unsigned int nComponents = InterpolatorConvertType::GetNumberOfComponents(value);
PixelType outputValue;
NumericTraits<PixelType>::SetLength( outputValue, nComponents );
for (unsigned int n = 0; n < nComponents; n++)
{
ComponentType component = InterpolatorConvertType::GetNthComponent( n, value );
if ( component < minComponent )
{
PixelConvertType::SetNthComponent( n, outputValue, static_cast<PixelComponentType>( minComponent ) );
}
else if ( component > maxComponent )
{
PixelConvertType::SetNthComponent( n, outputValue, static_cast<PixelComponentType>( maxComponent ) );
}
else
{
PixelConvertType::SetNthComponent(n, outputValue,
static_cast<PixelComponentType>( component ) );
}
}
return outputValue;
}
template<typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType>
typename StitchImageFilter<TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType>::InputImageVectorType
StitchImageFilter<TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType>
::GetInputs()
{
InputImageVectorType inputs;
for (unsigned int i = 0; i < this->GetNumberOfIndexedInputs(); ++i)
{
auto input = this->GetInput(i);
if (nullptr != input)
{
inputs.push_back(input);
}
}
return inputs;
}
template<typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType>
typename StitchImageFilter<TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType>::TransformMapType
StitchImageFilter<TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType>
::GetTransforms()
{
TransformMapType transforms;
for (unsigned int i = 0; i < this->GetNumberOfIndexedInputs(); ++i)
{
auto input = this->GetInput(i);
auto transform = this->GetTransform(i);
transforms[input] = transform;
}
return transforms;
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
void
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::GenerateInputRequestedRegion()
{
// Call the superclass' implementation of this method
Superclass::GenerateInputRequestedRegion();
if ( !this->GetInput() )
{
return;
}
// Get pointers to the input
auto inputs = this->GetInputs();
for (auto& input : inputs)
{
InputImagePointer inputPtr =
const_cast<TInputImage*>(input);
// Determining the actual input region is non-trivial, especially
// when we cannot assume anything about the transform being used.
// So we do the easy thing and request the entire input image.
//
inputPtr->SetRequestedRegionToLargestPossibleRegion();
}
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
void
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::GenerateOutputInformation()
{
// Call the superclass' implementation of this method
Superclass::GenerateOutputInformation();
// Get pointers to the input and output
OutputImageType *outputPtr = this->GetOutput();
if ( !outputPtr )
{
return;
}
const ReferenceImageBaseType *referenceImage = this->GetReferenceImage();
// Set the size of the output region
if ( m_UseReferenceImage && referenceImage )
{
outputPtr->SetLargestPossibleRegion(
referenceImage->GetLargestPossibleRegion() );
}
else
{
typename TOutputImage::RegionType outputLargestPossibleRegion;
outputLargestPossibleRegion.SetSize(m_Size);
outputLargestPossibleRegion.SetIndex(m_OutputStartIndex);
outputPtr->SetLargestPossibleRegion(outputLargestPossibleRegion);
}
// Set spacing and origin
if ( m_UseReferenceImage && referenceImage )
{
outputPtr->SetSpacing( referenceImage->GetSpacing() );
outputPtr->SetOrigin( referenceImage->GetOrigin() );
outputPtr->SetDirection( referenceImage->GetDirection() );
}
else
{
outputPtr->SetSpacing(m_OutputSpacing);
outputPtr->SetOrigin(m_OutputOrigin);
outputPtr->SetDirection(m_OutputDirection);
}
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
ModifiedTimeType
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::GetMTime(void) const
{
ModifiedTimeType latestTime = Object::GetMTime();
for (const auto& interpolator : m_Interpolators)
{
if (interpolator.second.GetPointer())
{
if (latestTime < interpolator.second->GetMTime())
{
latestTime = interpolator.second->GetMTime();
}
}
}
return latestTime;
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
void
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::PrintSelf(std::ostream & os, Indent indent) const
{
Superclass::PrintSelf(os, indent);
os << indent << "DefaultPixelValue: "
<< static_cast< typename NumericTraits< PixelType >::PrintType >
( m_DefaultPixelValue )
<< std::endl;
os << indent << "Size: " << m_Size << std::endl;
os << indent << "OutputStartIndex: " << m_OutputStartIndex << std::endl;
os << indent << "OutputSpacing: " << m_OutputSpacing << std::endl;
os << indent << "OutputOrigin: " << m_OutputOrigin << std::endl;
os << indent << "OutputDirection: " << m_OutputDirection << std::endl;
for (const auto& interpolator : m_Interpolators)
{
os << indent << "Interpolator: " << interpolator.second.GetPointer() << std::endl;
}
os << indent << "UseReferenceImage: " << ( m_UseReferenceImage ? "On" : "Off" )
<< std::endl;
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
void
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::EnsureTransforms()
{
const auto inputCount = this->GetNumberOfIndexedInputs();
for (unsigned int i = 0; i < inputCount; ++i)
{
auto input = this->GetInput(i);
if (nullptr == input)
{
itkExceptionMacro(<< "Nth input image is not set (n: " << i << ").");
}
auto transform = this->GetTransform(i);
if (nullptr == transform)
{
this->SetTransform(i, itk::IdentityTransform< TTransformPrecisionType, ImageDimension>::New().GetPointer());
}
}
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
void
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::EnsureInterpolators()
{
const auto inputCount = this->GetNumberOfIndexedInputs();
InterpolatorMapType newInterpolatorMap;
for (unsigned int i = 0; i < inputCount; ++i)
{
auto input = this->GetInput(i);
if (nullptr == input)
{
itkExceptionMacro(<< "Nth input image is not set (n: " << i << ").");
}
if (m_Interpolators[input].IsNull())
{
newInterpolatorMap[input] = LinearInterpolatorType::New().GetPointer();
}
else
{
newInterpolatorMap[input] = m_Interpolators[input];
}
}
m_Interpolators = newInterpolatorMap;
}
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType >
std::string
StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >
::GetTransformInputName(unsigned int index)
{
return "transform_" + std::to_string(index);
}
} // end namespace itk
#endif
diff --git a/Modules/MatchPointRegistration/src/Helper/mitkImageStitchingHelper.cpp b/Modules/MatchPointRegistration/src/Helper/mitkImageStitchingHelper.cpp
index e0fcb5f46a..1730a4f3ca 100644
--- a/Modules/MatchPointRegistration/src/Helper/mitkImageStitchingHelper.cpp
+++ b/Modules/MatchPointRegistration/src/Helper/mitkImageStitchingHelper.cpp
@@ -1,229 +1,229 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkImageStitchingHelper.h"
#include <itkInterpolateImageFunction.h>
#include <itkNearestNeighborInterpolateImageFunction.h>
#include <itkLinearInterpolateImageFunction.h>
#include <itkBSplineInterpolateImageFunction.h>
#include <itkWindowedSincInterpolateImageFunction.h>
#include <mitkImageAccessByItk.h>
#include <mitkImageCast.h>
#include <mitkGeometry3D.h>
#include <mitkImageToItk.h>
#include <mitkMAPAlgorithmHelper.h>
#include "mapRegistration.h"
#include "mitkRegistrationHelper.h"
template <typename TImage >
typename ::itk::InterpolateImageFunction< TImage >::Pointer generateInterpolator(mitk::ImageMappingInterpolator::Type interpolatorType)
{
typedef ::itk::InterpolateImageFunction< TImage > BaseInterpolatorType;
typename BaseInterpolatorType::Pointer result;
switch (interpolatorType)
{
case mitk::ImageMappingInterpolator::NearestNeighbor:
{
result = ::itk::NearestNeighborInterpolateImageFunction<TImage>::New();
break;
}
case mitk::ImageMappingInterpolator::BSpline_3:
{
typename ::itk::BSplineInterpolateImageFunction<TImage>::Pointer spInterpolator = ::itk::BSplineInterpolateImageFunction<TImage>::New();
spInterpolator->SetSplineOrder(3);
result = spInterpolator;
break;
}
case mitk::ImageMappingInterpolator::WSinc_Hamming:
{
result = ::itk::WindowedSincInterpolateImageFunction<TImage,4>::New();
break;
}
case mitk::ImageMappingInterpolator::WSinc_Welch:
{
result = ::itk::WindowedSincInterpolateImageFunction<TImage,4,::itk::Function::WelchWindowFunction<4> >::New();
break;
}
default:
{
result = ::itk::LinearInterpolateImageFunction<TImage>::New();
break;
}
}
return result;
};
template <typename TPixelType, unsigned int VImageDimension >
void doMITKStitching(const ::itk::Image<TPixelType,VImageDimension>* /*input1*/,
mitk::Image::Pointer& result,
std::vector<mitk::Image::ConstPointer> inputs,
std::vector<::map::core::RegistrationBase::ConstPointer> registrations,
const mitk::BaseGeometry* resultGeometry,
const double& paddingValue, itk::StitchStrategy stitchStrategy, mitk::ImageMappingInterpolator::Type interpolatorType)
{
using ConcreteRegistrationType = ::map::core::Registration<VImageDimension, VImageDimension>;
using ItkImageType = itk::Image<TPixelType, VImageDimension>;
using StitchingFilterType = ::itk::StitchImageFilter<ItkImageType, ItkImageType>;
auto stitcher = StitchingFilterType::New();
stitcher->SetDefaultPixelValue(paddingValue);
stitcher->SetOutputOrigin(resultGeometry->GetOrigin());
const auto spacing = resultGeometry->GetSpacing();
stitcher->SetOutputSpacing(spacing);
typename StitchingFilterType::DirectionType itkDirection;
const auto mitkDirection = resultGeometry->GetIndexToWorldTransform()->GetMatrix();
for (unsigned int i = 0; i < VImageDimension; ++i)
{
for (unsigned int j = 0; j < VImageDimension; ++j)
{
itkDirection[i][j] = mitkDirection[i][j] / spacing[j];
}
}
stitcher->SetOutputDirection(itkDirection);
typename ItkImageType::SizeType size;
size[0] = resultGeometry->GetExtent(0);
size[1] = resultGeometry->GetExtent(1);
size[2] = resultGeometry->GetExtent(2);
stitcher->SetSize(size);
- stitcher->SetNumberOfThreads(1);
+ stitcher->SetNumberOfWorkUnits(1);
stitcher->SetStitchStrategy(stitchStrategy);
auto inputIter = inputs.begin();
auto regIter = registrations.begin();
unsigned int index = 0;
while (inputIter != inputs.end())
{
auto itkInput = mitk::ImageToItkImage<TPixelType, VImageDimension>(*inputIter);
auto castedReg = dynamic_cast<const ConcreteRegistrationType*>(regIter->GetPointer());
auto kernel = dynamic_cast<const ::map::core::RegistrationKernel<VImageDimension, VImageDimension>* >(&(castedReg->getInverseMapping()));
if (nullptr == kernel)
{
mitkThrow() << "Cannot stitch images. At least passed registration object #"<<index<<" doesn't have a valid inverse mapping registration kernel.";
}
stitcher->SetInput(index, itkInput, kernel->getTransformModel(), generateInterpolator< ::itk::Image<TPixelType, VImageDimension> >(interpolatorType));
++inputIter;
++regIter;
++index;
}
stitcher->Update();
mitk::CastToMitkImage<>(stitcher->GetOutput(),result);
}
mitk::Image::Pointer
mitk::StitchImages(std::vector<Image::ConstPointer> inputs,
std::vector<::map::core::RegistrationBase::ConstPointer> registrations,
const BaseGeometry* resultGeometry,
const double& paddingValue, itk::StitchStrategy stitchStrategy,
mitk::ImageMappingInterpolator::Type interpolatorType)
{
if (inputs.size() != registrations.size())
{
mitkThrow() << "Cannot stitch images. Passed inputs vector and registrations vector have different sizes.";
}
if (inputs.empty())
{
mitkThrow() << "Cannot stitch images. No input images are defined.";
}
auto inputDim = inputs.front()->GetDimension();
auto inputPixelType = inputs.front()->GetPixelType();
for (const auto& input : inputs)
{
if (input->GetDimension() != inputDim)
{
mitkThrow() << "Cannot stitch images. Images have different dimensions. Dimeonsion of first input: " << inputDim << "; wrong dimension: " << input->GetDimension();
}
if (input->GetPixelType() != inputPixelType)
{
mitkThrow() << "Cannot stitch images. Input images have different pixeltypes. The current implementation does only support stitching of images with same pixel type. Dimeonsion of first input: " << inputPixelType.GetTypeAsString() << "; wrong dimension: " << input->GetPixelType().GetTypeAsString();
}
if (input->GetTimeSteps() > 1)
{
mitkThrow() << "Cannot stitch dynamic images. At least one input image has multiple time steps.";
}
}
for (const auto& reg : registrations)
{
if (reg->getMovingDimensions() != inputDim)
{
mitkThrow() << "Cannot stitch images. At least one registration has a different moving dimension then the inputs. Dimeonsion of inputs: " << inputDim << "; wrong dimension: " << reg->getMovingDimensions();
}
if (reg->getTargetDimensions() != inputDim)
{
mitkThrow() << "Cannot stitch images. At least one registration has a different target dimension then the inputs. Dimeonsion of inputs: " << inputDim << "; wrong dimension: " << reg->getTargetDimensions();
}
}
Image::Pointer result;
AccessFixedDimensionByItk_n(inputs.front(), doMITKStitching, 3, (result, inputs, registrations, resultGeometry, paddingValue, stitchStrategy, interpolatorType));
return result;
}
mitk::Image::Pointer
mitk::StitchImages(std::vector<Image::ConstPointer> inputs,
std::vector<MAPRegistrationWrapper::ConstPointer> registrations,
const BaseGeometry* resultGeometry,
const double& paddingValue, itk::StitchStrategy stitchStrategy,
mitk::ImageMappingInterpolator::Type interpolatorType)
{
std::vector<::map::core::RegistrationBase::ConstPointer> unwrappedRegs;
for (const auto& reg : registrations)
{
if (!reg)
{
mitkThrow() << "Cannot stitch images. At least one passed registration wrapper pointer is nullptr.";
}
unwrappedRegs.push_back(reg->GetRegistration());
}
Image::Pointer result = StitchImages(inputs, unwrappedRegs, resultGeometry, paddingValue, stitchStrategy, interpolatorType);
return result;
}
mitk::Image::Pointer
mitk::StitchImages(std::vector<Image::ConstPointer> inputs,
const BaseGeometry* resultGeometry,
const double& paddingValue, itk::StitchStrategy stitchStrategy,
mitk::ImageMappingInterpolator::Type interpolatorType)
{
auto defaultReg = GenerateIdentityRegistration3D();
std::vector<::map::core::RegistrationBase::ConstPointer> defaultRegs;
defaultRegs.resize(inputs.size());
std::fill(defaultRegs.begin(), defaultRegs.end(), defaultReg->GetRegistration());
Image::Pointer result = StitchImages(inputs, defaultRegs, resultGeometry, paddingValue, stitchStrategy, interpolatorType);
return result;
}
diff --git a/Modules/ModelFit/include/itkMaskedStatisticsImageFilter.hxx b/Modules/ModelFit/include/itkMaskedStatisticsImageFilter.hxx
index e6dea73005..63a3902636 100644
--- a/Modules/ModelFit/include/itkMaskedStatisticsImageFilter.hxx
+++ b/Modules/ModelFit/include/itkMaskedStatisticsImageFilter.hxx
@@ -1,393 +1,395 @@
/*=========================================================================
*
* Copyright Insight Software Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/
#ifndef __itkMaskedStatisticsImageFilter_hxx
#define __itkMaskedStatisticsImageFilter_hxx
#include "itkMaskedStatisticsImageFilter.h"
#include "itkImageScanlineIterator.h"
#include "itkProgressReporter.h"
namespace itk
{
template< typename TInputImage, typename TMaskImage >
MaskedStatisticsImageFilter< TInputImage, TMaskImage >
::MaskedStatisticsImageFilter():m_ThreadSum(1), m_SumOfSquares(1), m_Count(1), m_ThreadMin(1), m_ThreadMax(1)
{
+ this->DynamicMultiThreadingOff();
+
// first output is a copy of the image, DataObject created by
// superclass
//
// allocate the data objects for the outputs which are
// just decorators around pixel types
for ( int i = 1; i < 3; ++i )
{
typename PixelObjectType::Pointer output =
static_cast< PixelObjectType * >( this->MakeOutput(i).GetPointer() );
this->ProcessObject::SetNthOutput( i, output.GetPointer() );
}
// allocate the data objects for the outputs which are
// just decorators around real types
for ( int i = 3; i < 7; ++i )
{
typename RealObjectType::Pointer output =
static_cast< RealObjectType * >( this->MakeOutput(i).GetPointer() );
this->ProcessObject::SetNthOutput( i, output.GetPointer() );
}
this->GetMinimumOutput()->Set( NumericTraits< PixelType >::max() );
this->GetMaximumOutput()->Set( NumericTraits< PixelType >::NonpositiveMin() );
this->GetMeanOutput()->Set( NumericTraits< RealType >::max() );
this->GetSigmaOutput()->Set( NumericTraits< RealType >::max() );
this->GetVarianceOutput()->Set( NumericTraits< RealType >::max() );
this->GetSumOutput()->Set(NumericTraits< RealType >::Zero);
}
template< typename TInputImage, typename TMaskImage >
DataObject::Pointer
MaskedStatisticsImageFilter< TInputImage, TMaskImage >
::MakeOutput(DataObjectPointerArraySizeType output)
{
switch ( output )
{
case 0:
return TInputImage::New().GetPointer();
break;
case 1:
return PixelObjectType::New().GetPointer();
break;
case 2:
return PixelObjectType::New().GetPointer();
break;
case 3:
case 4:
case 5:
case 6:
return RealObjectType::New().GetPointer();
break;
default:
// might as well make an image
return TInputImage::New().GetPointer();
break;
}
}
template< typename TInputImage, typename TMaskImage >
typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::PixelObjectType *
MaskedStatisticsImageFilter< TInputImage, TMaskImage >
::GetMinimumOutput()
{
return static_cast< PixelObjectType * >( this->ProcessObject::GetOutput(1) );
}
template< typename TInputImage, typename TMaskImage >
const typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::PixelObjectType *
MaskedStatisticsImageFilter< TInputImage, TMaskImage >
::GetMinimumOutput() const
{
return static_cast< const PixelObjectType * >( this->ProcessObject::GetOutput(1) );
}
template< typename TInputImage, typename TMaskImage >
typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::PixelObjectType *
MaskedStatisticsImageFilter< TInputImage, TMaskImage >
::GetMaximumOutput()
{
return static_cast< PixelObjectType * >( this->ProcessObject::GetOutput(2) );
}
template< typename TInputImage, typename TMaskImage >
const typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::PixelObjectType *
MaskedStatisticsImageFilter< TInputImage, TMaskImage >
::GetMaximumOutput() const
{
return static_cast< const PixelObjectType * >( this->ProcessObject::GetOutput(2) );
}
template< typename TInputImage, typename TMaskImage >
typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::RealObjectType *
MaskedStatisticsImageFilter< TInputImage, TMaskImage >
::GetMeanOutput()
{
return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(3) );
}
template< typename TInputImage, typename TMaskImage >
const typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::RealObjectType *
MaskedStatisticsImageFilter< TInputImage, TMaskImage >
::GetMeanOutput() const
{
return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(3) );
}
template< typename TInputImage, typename TMaskImage >
typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::RealObjectType *
MaskedStatisticsImageFilter< TInputImage, TMaskImage >
::GetSigmaOutput()
{
return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(4) );
}
template< typename TInputImage, typename TMaskImage >
const typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::RealObjectType *
MaskedStatisticsImageFilter< TInputImage, TMaskImage >
::GetSigmaOutput() const
{
return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(4) );
}
template< typename TInputImage, typename TMaskImage >
typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::RealObjectType *
MaskedStatisticsImageFilter< TInputImage, TMaskImage >
::GetVarianceOutput()
{
return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(5) );
}
template< typename TInputImage, typename TMaskImage >
const typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::RealObjectType *
MaskedStatisticsImageFilter< TInputImage, TMaskImage >
::GetVarianceOutput() const
{
return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(5) );
}
template< typename TInputImage, typename TMaskImage >
typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::RealObjectType *
MaskedStatisticsImageFilter< TInputImage, TMaskImage >
::GetSumOutput()
{
return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(6) );
}
template< typename TInputImage, typename TMaskImage >
const typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::RealObjectType *
MaskedStatisticsImageFilter< TInputImage, TMaskImage >
::GetSumOutput() const
{
return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(6) );
}
template< typename TInputImage, typename TMaskImage >
void
MaskedStatisticsImageFilter< TInputImage, TMaskImage >
::GenerateInputRequestedRegion()
{
Superclass::GenerateInputRequestedRegion();
if ( this->GetInput() )
{
InputImagePointer image =
const_cast< typename Superclass::InputImageType * >( this->GetInput() );
image->SetRequestedRegionToLargestPossibleRegion();
}
}
template< typename TInputImage, typename TMaskImage >
void
MaskedStatisticsImageFilter< TInputImage, TMaskImage >
::EnlargeOutputRequestedRegion(DataObject *data)
{
Superclass::EnlargeOutputRequestedRegion(data);
data->SetRequestedRegionToLargestPossibleRegion();
}
template< typename TInputImage, typename TMaskImage >
void
MaskedStatisticsImageFilter< TInputImage, TMaskImage >
::AllocateOutputs()
{
// Pass the input through as the output
InputImagePointer image =
const_cast< TInputImage * >( this->GetInput() );
this->GraftOutput(image);
// Nothing that needs to be allocated for the remaining outputs
}
template< typename TInputImage, typename TMaskImage >
void
MaskedStatisticsImageFilter< TInputImage, TMaskImage >
::BeforeThreadedGenerateData()
{
- ThreadIdType numberOfThreads = this->GetNumberOfThreads();
+ ThreadIdType numberOfThreads = this->GetNumberOfWorkUnits();
// Resize the thread temporaries
m_Count.SetSize(numberOfThreads);
m_SumOfSquares.SetSize(numberOfThreads);
m_ThreadSum.SetSize(numberOfThreads);
m_ThreadMin.SetSize(numberOfThreads);
m_ThreadMax.SetSize(numberOfThreads);
// Initialize the temporaries
m_Count.Fill(NumericTraits< SizeValueType >::Zero);
m_ThreadSum.Fill(NumericTraits< RealType >::Zero);
m_SumOfSquares.Fill(NumericTraits< RealType >::Zero);
m_ThreadMin.Fill( NumericTraits< PixelType >::max() );
m_ThreadMax.Fill( NumericTraits< PixelType >::NonpositiveMin() );
}
template< typename TInputImage, typename TMaskImage >
void
MaskedStatisticsImageFilter< TInputImage, TMaskImage >
::AfterThreadedGenerateData()
{
ThreadIdType i;
SizeValueType count;
RealType sumOfSquares;
- ThreadIdType numberOfThreads = this->GetNumberOfThreads();
+ ThreadIdType numberOfThreads = this->GetNumberOfWorkUnits();
PixelType minimum;
PixelType maximum;
RealType mean;
RealType sigma;
RealType variance;
RealType sum;
sum = sumOfSquares = NumericTraits< RealType >::Zero;
count = 0;
// Find the min/max over all threads and accumulate count, sum and
// sum of squares
minimum = NumericTraits< PixelType >::max();
maximum = NumericTraits< PixelType >::NonpositiveMin();
for ( i = 0; i < numberOfThreads; i++ )
{
count += m_Count[i];
sum += m_ThreadSum[i];
sumOfSquares += m_SumOfSquares[i];
if ( m_ThreadMin[i] < minimum )
{
minimum = m_ThreadMin[i];
}
if ( m_ThreadMax[i] > maximum )
{
maximum = m_ThreadMax[i];
}
}
// compute statistics
mean = sum / static_cast< RealType >( count );
// unbiased estimate
variance = ( sumOfSquares - ( sum * sum / static_cast< RealType >( count ) ) )
/ ( static_cast< RealType >( count ) - 1 );
sigma = std::sqrt(variance);
// Set the outputs
this->GetMinimumOutput()->Set(minimum);
this->GetMaximumOutput()->Set(maximum);
this->GetMeanOutput()->Set(mean);
this->GetSigmaOutput()->Set(sigma);
this->GetVarianceOutput()->Set(variance);
this->GetSumOutput()->Set(sum);
}
template< typename TInputImage, typename TMaskImage >
void
MaskedStatisticsImageFilter< TInputImage, TMaskImage >
::ThreadedGenerateData(const RegionType & outputRegionForThread,
ThreadIdType threadId)
{
const SizeValueType size0 = outputRegionForThread.GetSize(0);
if( size0 == 0)
{
return;
}
RealType realValue;
PixelType value;
RealType sum = NumericTraits< RealType >::Zero;
RealType sumOfSquares = NumericTraits< RealType >::Zero;
SizeValueType count = NumericTraits< SizeValueType >::Zero;
PixelType min = NumericTraits< PixelType >::max();
PixelType max = NumericTraits< PixelType >::NonpositiveMin();
ImageScanlineConstIterator< TInputImage > it (this->GetInput(), outputRegionForThread);
// support progress methods/callbacks
const size_t numberOfLinesToProcess = outputRegionForThread.GetNumberOfPixels() / size0;
ProgressReporter progress( this, threadId, numberOfLinesToProcess );
// do the work
while ( !it.IsAtEnd() )
{
while ( !it.IsAtEndOfLine() )
{
bool isValid = true;
if(m_Mask.IsNotNull())
{
typename InputImageType::IndexType index = it.GetIndex();
typename InputImageType::PointType point;
this->GetInput()->TransformIndexToPhysicalPoint(index, point);
if (this->m_Mask->TransformPhysicalPointToIndex(point, index))
{
isValid = this->m_Mask->GetPixel(index) > 0.0;
};
}
if (isValid)
{
value = it.Get();
realValue = static_cast< RealType >( value );
if ( value < min )
{
min = value;
}
if ( value > max )
{
max = value;
}
sum += realValue;
sumOfSquares += ( realValue * realValue );
++count;
}
++it;
}
it.NextLine();
progress.CompletedPixel();
}
m_ThreadSum[threadId] = sum;
m_SumOfSquares[threadId] = sumOfSquares;
m_Count[threadId] = count;
m_ThreadMin[threadId] = min;
m_ThreadMax[threadId] = max;
}
template< typename TImage, typename TMaskImage >
void
MaskedStatisticsImageFilter< TImage, TMaskImage >
::PrintSelf(std::ostream & os, Indent indent) const
{
Superclass::PrintSelf(os, indent);
os << indent << "Minimum: "
<< static_cast< typename NumericTraits< PixelType >::PrintType >( this->GetMinimum() ) << std::endl;
os << indent << "Maximum: "
<< static_cast< typename NumericTraits< PixelType >::PrintType >( this->GetMaximum() ) << std::endl;
os << indent << "Sum: " << this->GetSum() << std::endl;
os << indent << "Mean: " << this->GetMean() << std::endl;
os << indent << "Sigma: " << this->GetSigma() << std::endl;
os << indent << "Variance: " << this->GetVariance() << std::endl;
}
} // end namespace itk
#endif
diff --git a/Modules/ModelFit/include/itkMultiOutputNaryFunctorImageFilter.tpp b/Modules/ModelFit/include/itkMultiOutputNaryFunctorImageFilter.tpp
index 09d16df573..6f2f9adb75 100644
--- a/Modules/ModelFit/include/itkMultiOutputNaryFunctorImageFilter.tpp
+++ b/Modules/ModelFit/include/itkMultiOutputNaryFunctorImageFilter.tpp
@@ -1,231 +1,233 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef __itkMultiOutputNaryFunctorImageFilter_hxx
#define __itkMultiOutputNaryFunctorImageFilter_hxx
#include "itkMultiOutputNaryFunctorImageFilter.h"
#include "itkImageRegionIterator.h"
#include "itkProgressReporter.h"
namespace itk
{
/**
* Constructor
*/
template< class TInputImage, class TOutputImage, class TFunction, class TMaskImage >
MultiOutputNaryFunctorImageFilter< TInputImage, TOutputImage, TFunction, TMaskImage >
::MultiOutputNaryFunctorImageFilter()
{
+ this->DynamicMultiThreadingOff();
+
// This number will be incremented each time an image
// is added over the two minimum required
this->SetNumberOfRequiredInputs(1);
this->ActualizeOutputs();
}
template< class TInputImage, class TOutputImage, class TFunction, class TMaskImage >
void
MultiOutputNaryFunctorImageFilter< TInputImage, TOutputImage, TFunction, TMaskImage >
::ActualizeOutputs()
{
this->SetNumberOfRequiredOutputs(m_Functor.GetNumberOfOutputs());
for (typename Superclass::DataObjectPointerArraySizeType i = this->GetNumberOfIndexedOutputs(); i< m_Functor.GetNumberOfOutputs(); ++i)
{
this->SetNthOutput( i, this->MakeOutput(i) );
}
while(this->GetNumberOfIndexedOutputs() > m_Functor.GetNumberOfOutputs())
{
this->RemoveOutput(this->GetNumberOfIndexedOutputs()-1);
}
};
/**
* ThreadedGenerateData Performs the pixel-wise addition
*/
template< class TInputImage, class TOutputImage, class TFunction, class TMaskImage >
void
MultiOutputNaryFunctorImageFilter< TInputImage, TOutputImage, TFunction, TMaskImage >
::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread,
ThreadIdType threadId)
{
ProgressReporter progress( this, threadId,
outputRegionForThread.GetNumberOfPixels() );
const unsigned int numberOfInputImages =
static_cast< unsigned int >( this->GetNumberOfIndexedInputs() );
const unsigned int numberOfOutputImages =
static_cast< unsigned int >( this->GetNumberOfIndexedOutputs() );
typedef ImageRegionConstIterator< TInputImage > ImageRegionConstIteratorType;
std::vector< ImageRegionConstIteratorType * > inputItrVector;
inputItrVector.reserve(numberOfInputImages);
typedef ImageRegionIterator< TOutputImage > OutputImageRegionIteratorType;
std::vector< OutputImageRegionIteratorType * > outputItrVector;
outputItrVector.reserve(numberOfOutputImages);
//check if mask image is set and generate iterator if mask is valid
typedef ImageRegionConstIterator< TMaskImage > MaskImageRegionIteratorType;
MaskImageRegionIteratorType* pMaskIterator = nullptr;
if (m_Mask.IsNotNull())
{
if (!m_Mask->GetLargestPossibleRegion().IsInside(outputRegionForThread))
{
itkExceptionMacro("Mask of filter is set but does not cover region of thread. Mask region: "<< m_Mask->GetLargestPossibleRegion() <<"Thread region: "<<outputRegionForThread)
}
pMaskIterator = new MaskImageRegionIteratorType(m_Mask,outputRegionForThread);
}
// go through the inputs and add iterators for non-null inputs
for ( unsigned int i = 0; i < numberOfInputImages; ++i )
{
InputImagePointer inputPtr =
dynamic_cast< TInputImage * >( ProcessObject::GetInput(i) );
if ( inputPtr )
{
inputItrVector.push_back( new ImageRegionConstIteratorType(inputPtr, outputRegionForThread) );
}
}
// go through the outputs and add iterators for non-null outputs
for ( unsigned int i = 0; i < numberOfOutputImages; ++i )
{
OutputImagePointer outputPtr =
dynamic_cast< TOutputImage * >( ProcessObject::GetOutput(i) );
if ( outputPtr )
{
outputItrVector.push_back( new OutputImageRegionIteratorType(outputPtr, outputRegionForThread) );
}
}
typename std::vector< ImageRegionConstIteratorType * >::iterator regionInputIterators;
const typename std::vector< ImageRegionConstIteratorType * >::const_iterator regionInputItEnd =
inputItrVector.end();
typename std::vector< OutputImageRegionIteratorType * >::iterator regionOutputIterators;
const typename std::vector< OutputImageRegionIteratorType * >::const_iterator regionOutputItEnd =
outputItrVector.end();
const unsigned int numberOfValidInputImages = inputItrVector.size();
const unsigned int numberOfValidOutputImages = outputItrVector.size();
if ( (numberOfValidInputImages != 0) && ( numberOfValidOutputImages != 0))
{
try
{
while ( !(outputItrVector.front()->IsAtEnd()) )
{
typename NaryInputArrayType::iterator arrayInIt;
typename NaryOutputArrayType::iterator arrayOutIt;
NaryInputArrayType naryInputArray(numberOfValidInputImages);
NaryOutputArrayType naryOutputArray(numberOfValidOutputImages);
bool isValid = true;
if (pMaskIterator)
{
isValid = pMaskIterator->Get() > 0;
++(*pMaskIterator);
}
arrayInIt = naryInputArray.begin();
regionInputIterators = inputItrVector.begin();
typename ImageRegionConstIteratorType::IndexType currentIndex;
if(regionInputIterators != regionInputItEnd)
{
currentIndex = ( *regionInputIterators )->GetIndex();
}
while ( regionInputIterators != regionInputItEnd )
{
*arrayInIt++ = ( *regionInputIterators )->Get();
++( *( *regionInputIterators ) );
++regionInputIterators;
}
if (isValid)
{
naryOutputArray = m_Functor(naryInputArray, currentIndex);
if (numberOfValidOutputImages != naryOutputArray.size())
{
itkExceptionMacro("Error. Number of valid output images do not equal number of outputs required by functor. Number of valid outputs: "<< numberOfValidOutputImages << "; needed output number:" << this->m_Functor.GetNumberOfOutputs());
}
}
else
{
for (typename NaryOutputArrayType::iterator pos = naryOutputArray.begin(); pos!= naryOutputArray.end(); ++pos)
{
*pos = 0.0;
}
}
arrayOutIt = naryOutputArray.begin();
regionOutputIterators = outputItrVector.begin();
while ( regionOutputIterators != regionOutputItEnd )
{
( *regionOutputIterators )->Set(*arrayOutIt++);
++( *( *regionOutputIterators ) );
++regionOutputIterators;
}
progress.CompletedPixel();
}
}
catch(...)
{
// Free memory in case of exceptions
regionInputIterators = inputItrVector.begin();
while ( regionInputIterators != regionInputItEnd )
{
delete ( *regionInputIterators++ );
}
regionOutputIterators = outputItrVector.begin();
while ( regionOutputIterators != regionOutputItEnd )
{
delete ( *regionOutputIterators++ );
}
delete pMaskIterator;
throw;
}
}
// Free memory regulary
regionInputIterators = inputItrVector.begin();
while ( regionInputIterators != regionInputItEnd )
{
delete ( *regionInputIterators++ );
}
regionOutputIterators = outputItrVector.begin();
while ( regionOutputIterators != regionOutputItEnd )
{
delete ( *regionOutputIterators++ );
}
delete pMaskIterator;
}
} // end namespace itk
#endif
diff --git a/Modules/ModelFit/include/mitkBinaryImageToLabelSetImageFilter.h b/Modules/ModelFit/include/mitkBinaryImageToLabelSetImageFilter.h
index 9c817e68ab..534417bed8 100644
--- a/Modules/ModelFit/include/mitkBinaryImageToLabelSetImageFilter.h
+++ b/Modules/ModelFit/include/mitkBinaryImageToLabelSetImageFilter.h
@@ -1,64 +1,64 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkBinaryImageToLabelSetImageFilter_h
#define mitkBinaryImageToLabelSetImageFilter_h
#include <mitkImageToImageFilter.h>
#include "mitkCommon.h"
#include "MitkModelFitExports.h"
namespace mitk
{
/** \brief Converts an binary image to a LabelSetImage. The amount of labels equals the connected components.
*/
class MITKMODELFIT_EXPORT BinaryImageToLabelSetImageFilter : public ImageToImageFilter
{
public:
mitkClassMacro(BinaryImageToLabelSetImageFilter, ImageToImageFilter);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
void SetInput(const InputImageType* image) override;
void SetInput(unsigned int index, const InputImageType* image) override;
itkSetMacro(FullyConnected, bool);
itkGetConstMacro(FullyConnected, bool);
itkSetMacro(ForegroundValue, unsigned int);
itkGetConstMacro(ForegroundValue, unsigned int);
itkSetMacro(OutputIsLabelSetImage, bool);
itkGetConstMacro(OutputIsLabelSetImage, bool);
private:
using Superclass::SetInput;
BinaryImageToLabelSetImageFilter() = default;
~BinaryImageToLabelSetImageFilter() override = default;
template <typename TPixel, unsigned int VImageDimension>
void ApplyBinaryImageToLabelMapFilter(const itk::Image<TPixel, VImageDimension>* inputImage);
void GenerateData() override;
- void VerifyInputInformation() override;
- void VerifyInputImage(const mitk::Image* inputImage);
+ void VerifyInputInformation() const override;
+ void VerifyInputImage(const mitk::Image* inputImage) const;
bool m_FullyConnected = true;
unsigned int m_ForegroundValue = 1;
bool m_OutputIsLabelSetImage = false;
};
}
#endif
diff --git a/Modules/ModelFit/include/mitkModelFitFunctorBase.h b/Modules/ModelFit/include/mitkModelFitFunctorBase.h
index 9500c86954..2fae18e611 100644
--- a/Modules/ModelFit/include/mitkModelFitFunctorBase.h
+++ b/Modules/ModelFit/include/mitkModelFitFunctorBase.h
@@ -1,137 +1,139 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MODEL_FIT_FUNCTOR_BASE_H
#define MODEL_FIT_FUNCTOR_BASE_H
#include <itkObject.h>
#include <mitkVector.h>
#include "mitkModelBase.h"
#include "mitkSVModelFitCostFunction.h"
#include "MitkModelFitExports.h"
+#include <mutex>
+
namespace mitk
{
class MITKMODELFIT_EXPORT ModelFitFunctorBase: public ::itk::Object
{
public:
typedef ModelFitFunctorBase Self;
typedef itk::Object Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
itkTypeMacro(ModelFitFunctorBase, itk::Object);
typedef ScalarType ParameterImagePixelType;
typedef std::vector<ParameterImagePixelType> InputPixelArrayType;
typedef std::vector<ParameterImagePixelType> OutputPixelArrayType;
/** Returns the values determined by fitting the passed model. The values in the returned vector are ordered in the
* following sequence:
* - model parameters (see also GetParameterNames())
* - derived model parameters (see also GetDerivedParameterNames())
* - criterion(s) (see also GetCriterionNames())
* - evaluation parameters (see also GetEvaluationParameterNames())
* @param value Signal the model should be fitted onto
* @param model Pointer to the preconfigured/ready to use model instance for the fitting against the signal curve
* @param initialParameters parameters of the model that should be used as starting point of the fitting process.
* @pre model must point to a valid instance.
* @pre Size of initialParameters must be equal to model->GetNumberOfParameters().
*/
OutputPixelArrayType Compute(const InputPixelArrayType& value, const ModelBase* model,
const ModelBase::ParametersType& initialParameters) const;
/** Returns the number of outputs the fit functor will return if compute is called.
* The number depends in parts on the passed model.
* @exception Exception will be thrown if no valid model is passed.*/
unsigned int GetNumberOfOutputs(const ModelBase* model) const;
typedef ModelBase::ParameterNamesType ParameterNamesType;
/** Returns names of all evaluation parameters defined by the user*/
ParameterNamesType GetEvaluationParameterNames() const;
void ResetEvaluationParameters();
void RegisterEvaluationParameter(const std::string& parameterName,
SVModelFitCostFunction* evaluationCostFunction);
const SVModelFitCostFunction* GetEvaluationParameterCostFunction(const std::string& parameterName)
const;
/** Returns names of the criterion used to fit the model. */
virtual ParameterNamesType GetCriterionNames() const = 0 ;
/** Returns names of the depug parameters generated by the functor.
Is empty, if debug is deactivated. */
ParameterNamesType GetDebugParameterNames() const;
itkBooleanMacro(DebugParameterMaps);
itkSetMacro(DebugParameterMaps, bool);
itkGetConstMacro(DebugParameterMaps, bool);
protected:
typedef ModelBase::ParametersType ParametersType;
typedef ModelFitCostFunctionInterface::SignalType SignalType;
ModelFitFunctorBase();
~ModelFitFunctorBase() override;
/**Internal Method called by Compute to get the final criterion values thar dove the fit.
must be implemented be concrete functor classes.*/
virtual OutputPixelArrayType GetCriteria(const ModelBase* model, const ParametersType& parameters,
const SignalType& sample) const = 0;
/** Internal Method called by Compute().
Gets all derived parameters of the models with the final found parameters of the fit.*/
OutputPixelArrayType GetDerivedParameters(const ModelBase* model,
const ParametersType& parameters) const;
/** Internal Method called by Compute().
Gets the evaluation parameters for all cost functions enlisted by the user, based on
the model with the final found parameters of the fit and the input signal.*/
OutputPixelArrayType GetEvaluationParameters(const ModelBase* model,
const ParametersType& parameters, const SignalType& sample) const;
typedef std::map<std::string, ParameterImagePixelType> DebugParameterMapType;
/** Internal Method called by Compute(). It does the real fit and returns the found parameters.
Additionally it must return its debug parameter via debugParameters.
@post If m_DebugParameterMaps is true, it must return all debug parameters defined by
GetDebugParameterNames() via debugParameters.
@param value Signal the Model should be fitted against
@param model Pointer to the model that should be fitted
@param initialParameters Initial modal parameters for the fit
@param [out] debugParameters Map containing all debug parameters for the done fit (must only valid if m_DebugParameterMap is true)*/
virtual ParametersType DoModelFit(const SignalType& value, const ModelBase* model,
const ModelBase::ParametersType& initialParameters,
DebugParameterMapType& debugParameters) const = 0;
/** Returns names of the depug parameters generated by the functor. Will be called by GetDebugParameterNames,
if debug is activated. */
virtual ParameterNamesType DefineDebugParameterNames()const = 0;
private:
typedef std::map<std::string, SVModelFitCostFunction::Pointer> CostFunctionMapType;
CostFunctionMapType m_CostFunctionMap;
bool m_DebugParameterMaps;
- ::itk::SimpleFastMutexLock m_Mutex;
+ mutable std::mutex m_Mutex;
};
}
#endif // MODEL_FIT_FUNCTOR_BASE_H
diff --git a/Modules/ModelFit/include/mitkModelFitInfo.h b/Modules/ModelFit/include/mitkModelFitInfo.h
index b9e849c7b9..a554018f41 100644
--- a/Modules/ModelFit/include/mitkModelFitInfo.h
+++ b/Modules/ModelFit/include/mitkModelFitInfo.h
@@ -1,198 +1,195 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkModelFitInfo_h
#define mitkModelFitInfo_h
-#include <itkMutexLockHolder.h>
-#include <itkSimpleFastMutexLock.h>
-
#include <mitkDataStorage.h>
#include "mitkModelFitConstants.h"
#include "mitkModelFitParameter.h"
#include "mitkModelFitStaticParameterMap.h"
#include "mitkScalarListLookupTable.h"
#include "mitkModelParameterizerBase.h"
#include "mitkModelTraitsInterface.h"
#include "MitkModelFitExports.h"
namespace mitk
{
namespace modelFit
{
/**
* @brief Data class that stores all information about a modelfit that is relevant to the
* visualization and stored as properties in the result nodes.
*/
class MITKMODELFIT_EXPORT ModelFitInfo : public itk::LightObject
{
public:
typedef std::string UIDType;
typedef std::vector<Parameter::Pointer> ParamListType;
typedef ParamListType::const_iterator ConstIterType;
mitkClassMacroItkParent(ModelFitInfo, itk::LightObject);
itkSimpleNewMacro(ModelFitInfo);
ModelFitInfo() :
x(mitk::ModelFitConstants::MODEL_X_VALUE_DEFAULT()),
xAxisName(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT()),
yAxisName(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT())
{
}
/**
* @brief Adds the given parameter to this fit's parameter list if it doesn't
* exist already.
* @param p The param that should be added to this fit's parameter list.
*/
void AddParameter(Parameter::Pointer p);
/**
* @brief Searches for the parameter with the given name and type in the fit's
* parameter list and returns it.
* @param name The name of the desired parameter.
* @param type The type of the desired parameter.
* @return The parameter with the given name on success or NULL otherwise.
*/
Parameter::ConstPointer GetParameter(const std::string& name, const Parameter::Type& type)
const;
/**
* @brief Searches for the parameter with the given name and type in the fit's
* parameter list and deletes it if it exists.
* @param name The name of the desired parameter.
* @param type The type of the desired parameter.
*/
void DeleteParameter(const std::string& name, const Parameter::Type& type);
/**Return const reference to the parameter list.*/
const ParamListType& GetParameters() const;
/** ModelFitConstants::MODEL_NAME_PROPERTY_NAME */
std::string modelName;
/** ModelFitConstants::MODEL_TYPE_PROPERTY_NAME */
std::string modelType;
/** ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME */
std::string function;
/** ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME */
std::string functionClassID;
/** ModelFitConstants::MODEL_X_PROPERTY_NAME */
std::string x;
/** ModelFitConstants::XAXIS_NAME_PROPERTY_NAME */
std::string xAxisName;
/** ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME */
std::string xAxisUnit;
/** ModelFitConstants::YAXIS_NAME_PROPERTY_NAME */
std::string yAxisName;
/** ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME */
std::string yAxisUnit;
/** ModelFitConstants::FIT_UID_PROPERTY_NAME */
UIDType uid;
/** ModelFitConstants::FIT_NAME_PROPERTY_NAME */
std::string fitName;
/** ModelFitConstants::FIT_TYPE_PROPERTY_NAME */
std::string fitType;
/** ModelFitConstants::FIT_STATIC_PARAMETERS_PROPERTY_NAME */
StaticParameterMap staticParamMap;
/** ModelFitConstants::FIT_INPUT_ROIUID_PROPERTY_NAME */
UIDType roiUID;
/** ModelFitConstants::FIT_INPUT_DATA_PROPERTY_NAME */
ScalarListLookupTable inputData;
mitk::Image::ConstPointer inputImage;
private:
typedef ParamListType::iterator IterType;
- typedef itk::MutexLockHolder<itk::SimpleFastMutexLock> LockType;
+ typedef std::lock_guard<std::mutex> LockType;
ParamListType parameterList;
- itk::SimpleFastMutexLock mutex;
+ std::mutex mutex;
};
/**
* @brief Reads the string property with the given name from the data of the given node
* and returns its value. Throws a ModelFitException if the property doesn't exist.
* @param node The node whose property value should be returned.
* @param prop The name of the property that should be read.
* @return The value of the found property.
* @throw ModelFitException If the property doesn't exist or returns an empty string.
*/
MITKMODELFIT_EXPORT const std::string GetMandatoryProperty(const mitk::DataNode* node,
const std::string& prop);
/**
* @brief Reads the string property with the given name from the given base data and
* returns its value. Throws a ModelFitException if the property doesn't exist.
* @param data The data whose property value should be returned.
* @param prop The name of the property that should be read.
* @return The value of the found property.
* @throw ModelFitException If the property doesn't exist or returns an empty string.
*/
MITKMODELFIT_EXPORT const std::string GetMandatoryProperty(const mitk::BaseData* data,
const std::string& prop);
/**
* @brief Creates a new ModelFitInfo instance from the nodes in the passed storage.
* The fit will be identified by the passed UID. Returns the instance on
* success.
* @param uid The uid of the fit that should get its ModelFitInfo created and which identifies the nodes in the storage.
* @param storage Pointer to the data storage containing any potential relevantThe nodes.
* @return The newly created modelfit on success or NULL otherwise.
*/
MITKMODELFIT_EXPORT ModelFitInfo::Pointer CreateFitInfoFromNode(const ModelFitInfo::UIDType& uid,
const mitk::DataStorage* storage);
/** creates a new ModelFitInfo instance from a passed modal instance and his traits instance*
* @param usedParameterizer Pointer to a model which was used for a fit, which should get a fit info created.
* @param inputImage Pointer to the input image. If it has no UID yet, a property will be added to the node.
* @param fitType String identifying the type of the fit (e.g. ROI based or voxel based)
* @param fitName Optional human readable name of the fit.
* @param roiUID UID of the ROI, if one was used.
* @return The newly created modelfit on success or NULL otherwise.*/
MITKMODELFIT_EXPORT ModelFitInfo::Pointer CreateFitInfoFromModelParameterizer(
const ModelParameterizerBase* usedParameterizer, mitk::BaseData* inputImage,
const std::string& fitType, const std::string& fitName = "", const ModelFitInfo::UIDType& roiUID = "");
/** @overload
Overloaded version that allows additional definition of optional input data for the fit.*/
MITKMODELFIT_EXPORT ModelFitInfo::Pointer CreateFitInfoFromModelParameterizer(
const ModelParameterizerBase* usedParameterizer, mitk::BaseData* inputImage,
const std::string& fitType, const ScalarListLookupTable& inputData, const std::string& fitName = "",
const ModelFitInfo::UIDType& roiUID = "");
/** Returns all nodes that belong to the fit indicated by the passed UID.
* @param fitUID The uid of the fit that is relevant for the query.
* @param storage Pointer to the data storage containing any potential relevant nodes.
* @return The set of found nodes or null if storage is not valid.
*/
MITKMODELFIT_EXPORT DataStorage::SetOfObjects::ConstPointer GetNodesOfFit(
const ModelFitInfo::UIDType& fitUID,
const mitk::DataStorage* storage);
typedef std::set<ModelFitInfo::UIDType> NodeUIDSetType;
/** Returns the UIDs of all fits that are derived (directly or indirectly from the passed node).
* @param node The node which defines the parent node. It will be searched in his derived nodes for fits.
* @param storage Pointer to the data storage containing any potential relevant nodes.
* @return The set of found uid will be returned.
*/
MITKMODELFIT_EXPORT NodeUIDSetType GetFitUIDsOfNode(const mitk::DataNode* node,
const mitk::DataStorage* storage);
}
}
#endif // mitkModelFit_h
diff --git a/Modules/ModelFit/src/Common/mitkBinaryImageToLabelSetImageFilter.cpp b/Modules/ModelFit/src/Common/mitkBinaryImageToLabelSetImageFilter.cpp
index 577374108c..f23020706a 100644
--- a/Modules/ModelFit/src/Common/mitkBinaryImageToLabelSetImageFilter.cpp
+++ b/Modules/ModelFit/src/Common/mitkBinaryImageToLabelSetImageFilter.cpp
@@ -1,96 +1,96 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkBinaryImageToLabelSetImageFilter.h"
#include <mitkImage.h>
#include <mitkLabelSetImage.h>
#include <mitkImageAccessByItk.h>
#include <mitkITKImageImport.h>
//itk
#include <itkBinaryImageToLabelMapFilter.h>
#include <itkLabelMapToLabelImageFilter.h>
template <typename TPixel, unsigned int VImageDimension>
void mitk::BinaryImageToLabelSetImageFilter::ApplyBinaryImageToLabelMapFilter(const itk::Image<TPixel, VImageDimension>* inputImage)
{
using ImageType = itk::Image<TPixel, VImageDimension>;
using BinaryImageToLabelMapFilterType = itk::BinaryImageToLabelMapFilter<ImageType>;
typename BinaryImageToLabelMapFilterType::Pointer binaryImageToLabelMapFilter = BinaryImageToLabelMapFilterType::New();
binaryImageToLabelMapFilter->SetInput(inputImage);
binaryImageToLabelMapFilter->SetInputForegroundValue(m_ForegroundValue);
binaryImageToLabelMapFilter->SetFullyConnected(m_FullyConnected);
using LabelMap2ImageType = itk::LabelMapToLabelImageFilter< typename BinaryImageToLabelMapFilterType::OutputImageType, ImageType>;
typename LabelMap2ImageType::Pointer label2image = LabelMap2ImageType::New();
label2image->SetInput(binaryImageToLabelMapFilter->GetOutput());
label2image->Update();
auto labeledImage = mitk::ImportItkImage(label2image->GetOutput());
if (m_OutputIsLabelSetImage) {
auto labeledBinaryImage = mitk::LabelSetImage::New();
labeledBinaryImage->InitializeByLabeledImage(labeledImage);
this->SetOutput(MakeNameFromOutputIndex(0), labeledBinaryImage.GetPointer());
}
else {
this->SetOutput(MakeNameFromOutputIndex(0), labeledImage.GetPointer());
}
}
- void mitk::BinaryImageToLabelSetImageFilter::VerifyInputImage(const mitk::Image* inputImage)
+ void mitk::BinaryImageToLabelSetImageFilter::VerifyInputImage(const mitk::Image* inputImage) const
{
if (!inputImage->IsInitialized())
mitkThrow() << "Input image is not initialized.";
if (!inputImage->IsVolumeSet())
mitkThrow() << "Input image volume is not set.";
auto geometry = inputImage->GetGeometry();
if (nullptr == geometry || !geometry->IsValid())
mitkThrow() << "Input image has invalid geometry.";
if (!geometry->GetImageGeometry())
mitkThrow() << "Geometry of input image is not an image geometry.";
}
void mitk::BinaryImageToLabelSetImageFilter::GenerateData()
{
const auto* inputImage = this->GetInput();
AccessByItk(inputImage, ApplyBinaryImageToLabelMapFilter);
}
void mitk::BinaryImageToLabelSetImageFilter::SetInput(const InputImageType* image)
{
if (this->GetInput() == image)
return;
Superclass::SetInput(image);
}
void mitk::BinaryImageToLabelSetImageFilter::SetInput(unsigned int index, const InputImageType* image)
{
if (0 != index)
mitkThrow() << "Input index " << index << " is invalid.";
this->SetInput(image);
}
-void mitk::BinaryImageToLabelSetImageFilter::VerifyInputInformation()
+void mitk::BinaryImageToLabelSetImageFilter::VerifyInputInformation() const
{
Superclass::VerifyInputInformation();
VerifyInputImage(this->GetInput());
}
diff --git a/Modules/ModelFit/src/Common/mitkTimeGridHelper.cpp b/Modules/ModelFit/src/Common/mitkTimeGridHelper.cpp
index 6d65d1d2eb..416439ae6f 100644
--- a/Modules/ModelFit/src/Common/mitkTimeGridHelper.cpp
+++ b/Modules/ModelFit/src/Common/mitkTimeGridHelper.cpp
@@ -1,89 +1,89 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkTimeGridHelper.h"
-#include "itkExceptionObject.h"
+#include "itkMacro.h"
bool mitk::TimeGridIsMonotonIncreasing(const mitk::ModelBase::TimeGridType timeGrid)
{
const auto beginPos = timeGrid.begin();
const auto endPos = timeGrid.end();
for(mitk::ModelBase::TimeGridType::const_iterator posTime = beginPos; posTime != endPos; ++posTime)
{
if (posTime != beginPos && *(posTime-1)<*posTime) return false;
}
return true;
};
mitk::ModelBase::ModelResultType mitk::InterpolateSignalToNewTimeGrid(const ModelBase::ModelResultType& inputSignal, const ModelBase::TimeGridType& inputGrid, const ModelBase::TimeGridType& outputGrid)
{
mitk::ModelBase::ModelResultType result(outputGrid.GetSize());
if (! inputSignal.GetSize())
{
return result;
}
if (inputSignal.GetSize() != inputGrid.GetSize())
{
itkGenericExceptionMacro("Input signal and input time grid have not the same size.");
}
mitk::ModelBase::ModelResultType::ValueType lastValue = inputSignal[0];
mitk::ModelBase::TimeGridType::ValueType lastTime = itk::NumericTraits<mitk::ModelBase::TimeGridType::ValueType>::NonpositiveMin();
mitk::ModelBase::TimeGridType::const_iterator posITime = inputGrid.begin();
mitk::ModelBase::ModelResultType::const_iterator posValue = inputSignal.begin();
mitk::ModelBase::ModelResultType::iterator posResult = result.begin();
for(mitk::ModelBase::TimeGridType::const_iterator posOTime = outputGrid.begin(); posOTime != outputGrid.end(); ++posResult, ++posOTime)
{
while(posITime!=inputGrid.end() && *posOTime > *posITime)
{ //forward in the input grid until the current output point
//is between last and the current input point.
lastValue = *posValue;
lastTime = *posITime;
++posValue;
++posITime;
}
double weightLast = 1 - (*posOTime - lastTime)/(*posITime - lastTime);
double weightNext = 1 - (*posITime - *posOTime)/(*posITime - lastTime);
*posResult = weightLast * lastValue + weightNext * (*posValue);
}
return result;
};
mitk::ModelBase::TimeGridType
mitk::GenerateSupersampledTimeGrid(const mitk::ModelBase::TimeGridType& grid, const unsigned int samplingRate)
{
unsigned int origGridSize = grid.size();
mitk::ModelBase::TimeGridType interpolatedTimeGrid(((origGridSize - 1) * samplingRate) + 1);
for (unsigned int t = 0; t < origGridSize - 1; ++t)
{
double delta = (grid[t + 1] - grid[t]) / samplingRate;
for (unsigned int i = 0; i < samplingRate; ++i)
{
interpolatedTimeGrid[(t * samplingRate) + i] = grid[t] + i * delta;
}
}
interpolatedTimeGrid[interpolatedTimeGrid.size() - 1] = grid[grid.size() - 1];
return interpolatedTimeGrid;
};
diff --git a/Modules/ModelFit/src/Functors/mitkModelFitFunctorBase.cpp b/Modules/ModelFit/src/Functors/mitkModelFitFunctorBase.cpp
index 7e025c4a21..1467eabd61 100644
--- a/Modules/ModelFit/src/Functors/mitkModelFitFunctorBase.cpp
+++ b/Modules/ModelFit/src/Functors/mitkModelFitFunctorBase.cpp
@@ -1,241 +1,241 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkModelFitFunctorBase.h"
mitk::ModelFitFunctorBase::OutputPixelArrayType
mitk::ModelFitFunctorBase::
Compute(const InputPixelArrayType& value, const ModelBase* model,
const ModelBase::ParametersType& initialParameters) const
{
if (!model)
{
itkExceptionMacro("Cannot compute fit. Passed model is not defined.");
}
if (model->GetNumberOfParameters() != initialParameters.Size())
{
itkExceptionMacro("Cannot compute fit. Parameter count of passed model and passed initial parameters differ. Model parameter count: "
<< model->GetNumberOfParameters() << "; Initial parameters: " << initialParameters);
}
SignalType sample(value.size());
for (SignalType::SizeValueType i = 0; i < sample.Size(); ++i)
{
sample[i] = value [i];
}
DebugParameterMapType debugParams;
ParameterNamesType debugNames;
if (this->m_DebugParameterMaps)
{
debugNames = this->GetDebugParameterNames();
}
ParametersType fittedParameters = DoModelFit(sample, model, initialParameters, debugParams);
OutputPixelArrayType derivedParameters = this->GetDerivedParameters(model, fittedParameters);
OutputPixelArrayType criteria = this->GetCriteria(model, fittedParameters, sample);
OutputPixelArrayType evaluationParameters = this->GetEvaluationParameters(model, fittedParameters,
sample);
if (criteria.size() != this->GetCriterionNames().size())
{
itkExceptionMacro("ModelFitInfo implementation seems to be inconsitent. Number of criterion values is not equal to number of criterion names.");
}
OutputPixelArrayType result(fittedParameters.Size() + derivedParameters.size() + criteria.size() +
evaluationParameters.size() + debugNames.size());
for (ParametersType::SizeValueType i = 0; i < fittedParameters.Size(); ++i)
{
result[i] = fittedParameters[i];
}
OutputPixelArrayType::size_type offset = fittedParameters.Size();
for (OutputPixelArrayType::size_type j = 0; j < derivedParameters.size(); ++j)
{
result[offset + j] = derivedParameters[j];
}
offset += derivedParameters.size();
for (OutputPixelArrayType::size_type j = 0; j < criteria.size(); ++j)
{
result[offset + j] = criteria[j];
}
offset += criteria.size();
for (OutputPixelArrayType::size_type j = 0; j < evaluationParameters.size(); ++j)
{
result[offset + j] = evaluationParameters[j];
}
offset += evaluationParameters.size();
for (OutputPixelArrayType::size_type j = 0; j < debugNames.size(); ++j)
{
DebugParameterMapType::const_iterator pos = debugParams.find(debugNames[j]);
if (pos == debugParams.end())
{
itkExceptionMacro("ModelFitInfo implementation seems to be inconsitent. Debug parameter defined by functor is not in its returned debug map. Invalid debug parameter name: "<<debugNames[j]);
}
else
{
result[offset + j] = pos->second;
}
}
return result;
};
unsigned int
mitk::ModelFitFunctorBase::GetNumberOfOutputs(const ModelBase* model) const
{
if (!model)
{
itkExceptionMacro("Cannot get number of outputs. Model is not defined.");
}
return model->GetNumberOfParameters() + model->GetNumberOfDerivedParameters() +
this->GetCriterionNames().size() + m_CostFunctionMap.size()+ this->GetDebugParameterNames().size();
};
void
mitk::ModelFitFunctorBase::ResetEvaluationParameters()
{
- m_Mutex.Lock();
+ m_Mutex.lock();
m_CostFunctionMap.clear();
- m_Mutex.Unlock();
+ m_Mutex.unlock();
};
void
mitk::ModelFitFunctorBase::RegisterEvaluationParameter(const std::string& parameterName,
SVModelFitCostFunction* evaluationCostFunction)
{
- m_Mutex.Lock();
+ m_Mutex.lock();
SVModelFitCostFunction::Pointer costFunctPtr = evaluationCostFunction;
m_CostFunctionMap.insert(std::make_pair(parameterName, costFunctPtr));
- m_Mutex.Unlock();
+ m_Mutex.unlock();
};
mitk::ModelFitFunctorBase::ParameterNamesType
mitk::ModelFitFunctorBase::GetEvaluationParameterNames() const
{
- m_Mutex.Lock();
+ m_Mutex.lock();
ParameterNamesType result;
for (CostFunctionMapType::const_iterator pos = m_CostFunctionMap.begin();
pos != m_CostFunctionMap.end(); ++pos)
{
result.push_back(pos->first);
}
- m_Mutex.Unlock();
+ m_Mutex.unlock();
return result;
};
const mitk::SVModelFitCostFunction*
mitk::ModelFitFunctorBase::GetEvaluationParameterCostFunction(const std::string& parameterName)
const
{
const SVModelFitCostFunction* result = nullptr;
- m_Mutex.Lock();
+ m_Mutex.lock();
CostFunctionMapType::const_iterator pos = m_CostFunctionMap.find(parameterName);
if (pos != m_CostFunctionMap.end())
{
result = (pos->second).GetPointer();
}
- m_Mutex.Unlock();
+ m_Mutex.unlock();
return result;
};
mitk::ModelFitFunctorBase::ParameterNamesType
mitk::ModelFitFunctorBase::GetDebugParameterNames() const
{
ParameterNamesType result;
if (this->m_DebugParameterMaps)
{
result = this->DefineDebugParameterNames();
}
return result;
};
mitk::ModelFitFunctorBase::
ModelFitFunctorBase() : m_DebugParameterMaps(false)
{};
mitk::ModelFitFunctorBase::
~ModelFitFunctorBase() {};
mitk::ModelFitFunctorBase::OutputPixelArrayType
mitk::ModelFitFunctorBase::GetDerivedParameters(const ModelBase* model,
const ParametersType& parameters) const
{
ModelBase::DerivedParameterMapType derivedParameterMap = model->GetDerivedParameters(parameters);
OutputPixelArrayType result(derivedParameterMap.size());
unsigned int i = 0;
for (ModelBase::DerivedParameterMapType::const_iterator pos = derivedParameterMap.begin();
pos != derivedParameterMap.end(); ++pos, ++i)
{
result[i] = pos->second;
}
return result;
};
mitk::ModelFitFunctorBase::OutputPixelArrayType
mitk::ModelFitFunctorBase::GetEvaluationParameters(const ModelBase* model,
const ParametersType& parameters, const SignalType& sample) const
{
- m_Mutex.Lock();
+ m_Mutex.lock();
OutputPixelArrayType result(m_CostFunctionMap.size());
unsigned int i = 0;
for (CostFunctionMapType::const_iterator pos = m_CostFunctionMap.begin();
pos != m_CostFunctionMap.end(); ++pos, ++i)
{
//break constness to configure evaluation cost functions. This operatoin is guarded be the mutex
//after costFct->GetValue() the cost function may change its state again and is irrelevant for the
//current call of GetEvaluationParameters
SVModelFitCostFunction* costFct = const_cast<SVModelFitCostFunction*>(pos->second.GetPointer());
costFct->SetModel(model);
costFct->SetSample(sample);
result[i] = costFct->GetValue(parameters);
}
- m_Mutex.Unlock();
+ m_Mutex.unlock();
return result;
};
diff --git a/Modules/ModelFit/test/itkMaskedStatisticsImageFilterTest.cpp b/Modules/ModelFit/test/itkMaskedStatisticsImageFilterTest.cpp
index 5a00710a8e..590937f13f 100644
--- a/Modules/ModelFit/test/itkMaskedStatisticsImageFilterTest.cpp
+++ b/Modules/ModelFit/test/itkMaskedStatisticsImageFilterTest.cpp
@@ -1,76 +1,76 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "itkImage.h"
#include "itkImageRegionIterator.h"
#include "itkMaskedStatisticsImageFilter.h"
#include "mitkTestingMacros.h"
#include "mitkVector.h"
#include "mitkTestDynamicImageGenerator.h"
int itkMaskedStatisticsImageFilterTest(int /*argc*/, char*[] /*argv[]*/)
{
// always start with this!
MITK_TEST_BEGIN("itkMaskedStatisticsImageFilterTest")
//Prepare test artifacts and helper
mitk::TestImageType::Pointer img1 = mitk::GenerateTestImage();
typedef itk::MaskedStatisticsImageFilter<mitk::TestImageType> FilterType;
FilterType::Pointer testFilter = FilterType::New();
testFilter->SetInput(img1);
- testFilter->SetNumberOfThreads(2);
+ testFilter->SetNumberOfWorkUnits(2);
testFilter->Update();
FilterType::PixelType max = testFilter->GetMaximum();
FilterType::PixelType min = testFilter->GetMinimum();
FilterType::RealType mean = testFilter->GetMean();
FilterType::RealType sig = testFilter->GetSigma();
FilterType::RealType variance = testFilter->GetVariance();
FilterType::RealType sum = testFilter->GetSum();
CPPUNIT_ASSERT_MESSAGE("Check computed maximum",9 == max);
CPPUNIT_ASSERT_MESSAGE("Check computed minimum",1 == min);
CPPUNIT_ASSERT_MESSAGE("Check computed mean",5 == mean);
CPPUNIT_ASSERT_MESSAGE("Check computed sigma",sqrt(7.5) == sig);
CPPUNIT_ASSERT_MESSAGE("Check computed variance",7.5 == variance);
CPPUNIT_ASSERT_MESSAGE("Check computed sum",45 == sum);
//Test with mask set
mitk::TestMaskType::Pointer mask = mitk::GenerateTestMask();
testFilter->SetMask(mask);
testFilter->Update();
max = testFilter->GetMaximum();
min = testFilter->GetMinimum();
mean = testFilter->GetMean();
sig = testFilter->GetSigma();
variance = testFilter->GetVariance();
sum = testFilter->GetSum();
CPPUNIT_ASSERT_MESSAGE("Check computed maximum",4 == max);
CPPUNIT_ASSERT_MESSAGE("Check computed minimum",2 == min);
CPPUNIT_ASSERT_MESSAGE("Check computed mean",3 == mean);
CPPUNIT_ASSERT_MESSAGE("Check computed sigma",1 == sig);
CPPUNIT_ASSERT_MESSAGE("Check computed variance",1 == variance);
CPPUNIT_ASSERT_MESSAGE("Check computed sum",9 == sum);
MITK_TEST_END()
}
diff --git a/Modules/ModelFit/test/itkMultiOutputNaryFunctorImageFilterTest.cpp b/Modules/ModelFit/test/itkMultiOutputNaryFunctorImageFilterTest.cpp
index f38866b609..4ca0102a9e 100644
--- a/Modules/ModelFit/test/itkMultiOutputNaryFunctorImageFilterTest.cpp
+++ b/Modules/ModelFit/test/itkMultiOutputNaryFunctorImageFilterTest.cpp
@@ -1,218 +1,218 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "itkImage.h"
#include "itkImageRegionIterator.h"
#include "itkMultiOutputNaryFunctorImageFilter.h"
#include "mitkTestingMacros.h"
#include "mitkVector.h"
#include "mitkTestDynamicImageGenerator.h"
class TestFunctor
{
public:
typedef std::vector<int> InputPixelArrayType;
typedef std::vector<int> OutputPixelArrayType;
typedef itk::Index<2> IndexType;
TestFunctor()
{
secondOutputSelection = 0;
};
~TestFunctor() {};
int secondOutputSelection;
unsigned int GetNumberOfOutputs() const
{
return 4;
}
bool operator!=( const TestFunctor & other) const
{
return !(*this == other);
}
bool operator==( const TestFunctor & other ) const
{
return secondOutputSelection == other.secondOutputSelection;
}
inline OutputPixelArrayType operator()( const InputPixelArrayType & value, const IndexType& currentIndex ) const
{
OutputPixelArrayType result;
int sum = 0;
for (InputPixelArrayType::const_iterator pos = value.begin(); pos != value.end(); ++pos)
{
sum += *pos;
}
result.push_back(sum);
result.push_back(value[secondOutputSelection]);
result.push_back(currentIndex[0]);
result.push_back(currentIndex[1]);
return result;
}
};
int itkMultiOutputNaryFunctorImageFilterTest(int /*argc*/, char*[] /*argv[]*/)
{
// always start with this!
MITK_TEST_BEGIN("itkMultiOutputNaryFunctorImageFilter")
//Prepare test artifacts and helper
mitk::TestImageType::Pointer img1 = mitk::GenerateTestImage();
mitk::TestImageType::Pointer img2 = mitk::GenerateTestImage(10);
mitk::TestImageType::Pointer img3 = mitk::GenerateTestImage(100);
mitk::TestImageType::IndexType testIndex1;
testIndex1[0] = 0;
testIndex1[1] = 0;
mitk::TestImageType::IndexType testIndex2;
testIndex2[0] = 2;
testIndex2[1] = 0;
mitk::TestImageType::IndexType testIndex3;
testIndex3[0] = 0;
testIndex3[1] = 1;
mitk::TestImageType::IndexType testIndex4;
testIndex4[0] = 1;
testIndex4[1] = 1;
mitk::TestImageType::IndexType testIndex5;
testIndex5[0] = 2;
testIndex5[1] = 2;
//Test default usage of filter
typedef itk::MultiOutputNaryFunctorImageFilter<mitk::TestImageType,mitk::TestImageType,TestFunctor> FilterType;
FilterType::Pointer testFilter = FilterType::New();
testFilter->SetInput(0,img1);
testFilter->SetInput(1,img2);
testFilter->SetInput(2,img3);
- testFilter->SetNumberOfThreads(2);
+ testFilter->SetNumberOfWorkUnits(2);
testFilter->Update();
mitk::TestImageType::Pointer out1 = testFilter->GetOutput(0);
mitk::TestImageType::Pointer out2 = testFilter->GetOutput(1);
mitk::TestImageType::Pointer out3 = testFilter->GetOutput(2);
mitk::TestImageType::Pointer out4 = testFilter->GetOutput(3);
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #1 index #1 (functor #1)",111 == out1->GetPixel(testIndex1));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #1 index #2 (functor #1)",333 == out1->GetPixel(testIndex2));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #1 index #3 (functor #1)",444 == out1->GetPixel(testIndex3));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #1 index #4 (functor #1)",555 == out1->GetPixel(testIndex4));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #1 index #5 (functor #1)",999 == out1->GetPixel(testIndex5));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #2 index #1 (functor #1)",1 == out2->GetPixel(testIndex1));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #2 index #2 (functor #1)",3 == out2->GetPixel(testIndex2));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #2 index #3 (functor #1)",4 == out2->GetPixel(testIndex3));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #2 index #4 (functor #1)",5 == out2->GetPixel(testIndex4));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #2 index #5 (functor #1)",9 == out2->GetPixel(testIndex5));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #3 index #1 (functor #1)",0 == out3->GetPixel(testIndex1));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #3 index #2 (functor #1)",2 == out3->GetPixel(testIndex2));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #3 index #3 (functor #1)",0 == out3->GetPixel(testIndex3));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #3 index #4 (functor #1)",1 == out3->GetPixel(testIndex4));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #3 index #5 (functor #1)",2 == out3->GetPixel(testIndex5));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #4 index #1 (functor #1)",0 == out4->GetPixel(testIndex1));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #4 index #2 (functor #1)",0 == out4->GetPixel(testIndex2));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #4 index #3 (functor #1)",1 == out4->GetPixel(testIndex3));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #4 index #4 (functor #1)",1 == out4->GetPixel(testIndex4));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #4 index #5 (functor #1)",2 == out4->GetPixel(testIndex5));
//Test with functor set by user
TestFunctor funct2;
funct2.secondOutputSelection = 1;
testFilter->SetFunctor(funct2);
testFilter->Update();
out1 = testFilter->GetOutput(0);
out2 = testFilter->GetOutput(1);
out3 = testFilter->GetOutput(2);
out4 = testFilter->GetOutput(3);
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #1 index #1 (functor #2)",111 == out1->GetPixel(testIndex1));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #1 index #2 (functor #2)",333 == out1->GetPixel(testIndex2));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #1 index #3 (functor #2)",444 == out1->GetPixel(testIndex3));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #1 index #4 (functor #2)",555 == out1->GetPixel(testIndex4));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #1 index #5 (functor #2)",999 == out1->GetPixel(testIndex5));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #2 index #1 (functor #2)",10 == out2->GetPixel(testIndex1));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #2 index #2 (functor #2)",30 == out2->GetPixel(testIndex2));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #2 index #3 (functor #2)",40 == out2->GetPixel(testIndex3));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #2 index #4 (functor #2)",50 == out2->GetPixel(testIndex4));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #2 index #5 (functor #2)",90 == out2->GetPixel(testIndex5));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #3 index #1 (functor #2)",0 == out3->GetPixel(testIndex1));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #3 index #2 (functor #2)",2 == out3->GetPixel(testIndex2));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #3 index #3 (functor #2)",0 == out3->GetPixel(testIndex3));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #3 index #4 (functor #2)",1 == out3->GetPixel(testIndex4));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #3 index #5 (functor #2)",2 == out3->GetPixel(testIndex5));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #4 index #1 (functor #2)",0 == out4->GetPixel(testIndex1));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #4 index #2 (functor #2)",0 == out4->GetPixel(testIndex2));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #4 index #3 (functor #2)",1 == out4->GetPixel(testIndex3));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #4 index #4 (functor #2)",1 == out4->GetPixel(testIndex4));
CPPUNIT_ASSERT_MESSAGE("Check pixel of output #4 index #5 (functor #2)",2 == out4->GetPixel(testIndex5));
//Test with mask set
mitk::TestMaskType::Pointer mask = mitk::GenerateTestMask();
testFilter->SetMask(mask);
testFilter->Update();
out1 = testFilter->GetOutput(0);
out2 = testFilter->GetOutput(1);
out3 = testFilter->GetOutput(2);
out4 = testFilter->GetOutput(3);
CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #1 index #1 (functor #2)",0 == out1->GetPixel(testIndex1));
CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #1 index #2 (functor #2)",333 == out1->GetPixel(testIndex2));
CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #1 index #3 (functor #2)",444 == out1->GetPixel(testIndex3));
CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #1 index #4 (functor #2)",0 == out1->GetPixel(testIndex4));
CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #1 index #5 (functor #2)",0 == out1->GetPixel(testIndex5));
CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #2 index #1 (functor #2)",0 == out2->GetPixel(testIndex1));
CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #2 index #2 (functor #2)",30 == out2->GetPixel(testIndex2));
CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #2 index #3 (functor #2)",40 == out2->GetPixel(testIndex3));
CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #2 index #4 (functor #2)",0 == out2->GetPixel(testIndex4));
CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #2 index #5 (functor #2)",0 == out2->GetPixel(testIndex5));
CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #3 index #1 (functor #2)",0 == out3->GetPixel(testIndex1));
CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #3 index #2 (functor #2)",2 == out3->GetPixel(testIndex2));
CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #3 index #3 (functor #2)",0 == out3->GetPixel(testIndex3));
CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #3 index #4 (functor #2)",0 == out3->GetPixel(testIndex4));
CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #3 index #5 (functor #2)",0 == out3->GetPixel(testIndex5));
CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #4 index #1 (functor #2)",0 == out4->GetPixel(testIndex1));
CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #4 index #2 (functor #2)",0 == out4->GetPixel(testIndex2));
CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #4 index #3 (functor #2)",1 == out4->GetPixel(testIndex3));
CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #4 index #4 (functor #2)",0 == out4->GetPixel(testIndex4));
CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #4 index #5 (functor #2)",0 == out4->GetPixel(testIndex5));
MITK_TEST_END()
}
diff --git a/Modules/Multilabel/CMakeLists.txt b/Modules/Multilabel/CMakeLists.txt
index 51a2a06fd3..ff1f469fad 100644
--- a/Modules/Multilabel/CMakeLists.txt
+++ b/Modules/Multilabel/CMakeLists.txt
@@ -1,10 +1,11 @@
mitk_create_module(
DEPENDS MitkCore MitkAlgorithmsExt MitkSceneSerializationBase MitkDICOMQI
+ PACKAGE_DEPENDS ITK|Smoothing
)
add_subdirectory(autoload/IO)
add_subdirectory(autoload/DICOMSegIO)
if(BUILD_TESTING)
add_subdirectory(Testing)
endif()
diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp
index da0754faf6..25f2bd524d 100644
--- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp
+++ b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp
@@ -1,654 +1,654 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef __mitkLabelSetImageWriter__cpp
#define __mitkLabelSetImageWriter__cpp
#include "mitkLabelSetImageIO.h"
#include "mitkBasePropertySerializer.h"
#include "mitkIOMimeTypes.h"
#include "mitkImageAccessByItk.h"
#include "mitkLabelSetIOHelper.h"
#include "mitkLabelSetImageConverter.h"
#include <mitkLocaleSwitch.h>
#include <mitkArbitraryTimeGeometry.h>
#include <mitkIPropertyPersistence.h>
#include <mitkCoreServices.h>
#include <mitkItkImageIO.h>
#include <mitkUIDManipulator.h>
// itk
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkMetaDataDictionary.h"
#include "itkMetaDataObject.h"
#include "itkNrrdImageIO.h"
#include <tinyxml2.h>
namespace mitk
{
const char* const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type";
const char* const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints";
const char* const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type";
const char* const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints";
const char* const PROPERTY_KEY_UID = "org_mitk_uid";
LabelSetImageIO::LabelSetImageIO()
: AbstractFileIO(LabelSetImage::GetStaticNameOfClass(), IOMimeTypes::NRRD_MIMETYPE(), "MITK Multilabel Image")
{
AbstractFileWriter::SetRanking(10);
AbstractFileReader::SetRanking(10);
this->RegisterService();
}
IFileIO::ConfidenceLevel LabelSetImageIO::GetWriterConfidenceLevel() const
{
if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported)
return Unsupported;
const auto *input = static_cast<const LabelSetImage *>(this->GetInput());
if (input)
return Supported;
else
return Unsupported;
}
void LabelSetImageIO::Write()
{
ValidateOutputLocation();
auto input = dynamic_cast<const LabelSetImage *>(this->GetInput());
mitk::LocaleSwitch localeSwitch("C");
mitk::Image::Pointer inputVector = mitk::ConvertLabelSetImageToImage(input);
// image write
if (inputVector.IsNull())
{
mitkThrow() << "Cannot write non-image data";
}
itk::NrrdImageIO::Pointer nrrdImageIo = itk::NrrdImageIO::New();
// Clone the image geometry, because we might have to change it
// for writing purposes
BaseGeometry::Pointer geometry = inputVector->GetGeometry()->Clone();
// Check if geometry information will be lost
if (inputVector->GetDimension() == 2 && !geometry->Is2DConvertable())
{
MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might "
"consider using Convert2Dto3DImageFilter before saving.";
// set matrix to identity
mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New();
affTrans->SetIdentity();
mitk::Vector3D spacing = geometry->GetSpacing();
mitk::Point3D origin = geometry->GetOrigin();
geometry->SetIndexToWorldTransform(affTrans);
geometry->SetSpacing(spacing);
geometry->SetOrigin(origin);
}
LocalFile localFile(this);
const std::string path = localFile.GetFileName();
MITK_INFO << "Writing image: " << path << std::endl;
try
{
// Implementation of writer using itkImageIO directly. This skips the use
// of templated itkImageFileWriter, which saves the multiplexing on MITK side.
const unsigned int dimension = inputVector->GetDimension();
const unsigned int *const dimensions = inputVector->GetDimensions();
const mitk::PixelType pixelType = inputVector->GetPixelType();
const mitk::Vector3D mitkSpacing = geometry->GetSpacing();
const mitk::Point3D mitkOrigin = geometry->GetOrigin();
// Due to templating in itk, we are forced to save a 4D spacing and 4D Origin,
// though they are not supported in MITK
itk::Vector<double, 4u> spacing4D;
spacing4D[0] = mitkSpacing[0];
spacing4D[1] = mitkSpacing[1];
spacing4D[2] = mitkSpacing[2];
spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here
itk::Vector<double, 4u> origin4D;
origin4D[0] = mitkOrigin[0];
origin4D[1] = mitkOrigin[1];
origin4D[2] = mitkOrigin[2];
origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here
// Set the necessary information for imageIO
nrrdImageIo->SetNumberOfDimensions(dimension);
nrrdImageIo->SetPixelType(pixelType.GetPixelType());
- nrrdImageIo->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ?
- static_cast<itk::ImageIOBase::IOComponentType>(pixelType.GetComponentType()) :
- itk::ImageIOBase::UNKNOWNCOMPONENTTYPE);
+ nrrdImageIo->SetComponentType(static_cast<int>(pixelType.GetComponentType()) < PixelComponentUserType
+ ? pixelType.GetComponentType()
+ : itk::IOComponentEnum::UNKNOWNCOMPONENTTYPE);
nrrdImageIo->SetNumberOfComponents(pixelType.GetNumberOfComponents());
itk::ImageIORegion ioRegion(dimension);
for (unsigned int i = 0; i < dimension; i++)
{
nrrdImageIo->SetDimensions(i, dimensions[i]);
nrrdImageIo->SetSpacing(i, spacing4D[i]);
nrrdImageIo->SetOrigin(i, origin4D[i]);
- mitk::Vector3D mitkDirection;
- mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i));
+ mitk::Vector3D mitkDirection(0.0);
+ mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i).as_ref());
itk::Vector<double, 4u> direction4D;
direction4D[0] = mitkDirection[0];
direction4D[1] = mitkDirection[1];
direction4D[2] = mitkDirection[2];
// MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must
// save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix.
if (i == 3)
{
direction4D[3] = 1; // homogenous component
}
else
{
direction4D[3] = 0;
}
vnl_vector<double> axisDirection(dimension);
for (unsigned int j = 0; j < dimension; j++)
{
axisDirection[j] = direction4D[j] / spacing4D[i];
}
nrrdImageIo->SetDirection(i, axisDirection);
ioRegion.SetSize(i, inputVector->GetLargestPossibleRegion().GetSize(i));
ioRegion.SetIndex(i, inputVector->GetLargestPossibleRegion().GetIndex(i));
}
// use compression if available
nrrdImageIo->UseCompressionOn();
nrrdImageIo->SetIORegion(ioRegion);
nrrdImageIo->SetFileName(path);
// label set specific meta data
char keybuffer[512];
char valbuffer[512];
sprintf(keybuffer, "modality");
sprintf(valbuffer, "org.mitk.image.multilabel");
itk::EncapsulateMetaData<std::string>(
nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer));
sprintf(keybuffer, "layers");
sprintf(valbuffer, "%1d", input->GetNumberOfLayers());
itk::EncapsulateMetaData<std::string>(
nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer));
for (unsigned int layerIdx = 0; layerIdx < input->GetNumberOfLayers(); layerIdx++)
{
sprintf(keybuffer, "layer_%03u", layerIdx); // layer idx
sprintf(valbuffer, "%1u", input->GetNumberOfLabels(layerIdx)); // number of labels for the layer
itk::EncapsulateMetaData<std::string>(
nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer));
auto iter = input->GetLabelSet(layerIdx)->IteratorConstBegin();
unsigned int count(0);
while (iter != input->GetLabelSet(layerIdx)->IteratorConstEnd())
{
tinyxml2::XMLDocument document;
document.InsertEndChild(document.NewDeclaration());
auto *labelElem = mitk::LabelSetIOHelper::GetLabelAsXMLElement(document, iter->second);
document.InsertEndChild(labelElem);
tinyxml2::XMLPrinter printer;
document.Print(&printer);
sprintf(keybuffer, "org.mitk.label_%03u_%05u", layerIdx, count);
itk::EncapsulateMetaData<std::string>(
nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), printer.CStr());
++iter;
++count;
}
}
// end label set specific meta data
// Handle time geometry
const auto* arbitraryTG = dynamic_cast<const ArbitraryTimeGeometry*>(input->GetTimeGeometry());
if (arbitraryTG)
{
itk::EncapsulateMetaData<std::string>(nrrdImageIo->GetMetaDataDictionary(),
PROPERTY_KEY_TIMEGEOMETRY_TYPE,
ArbitraryTimeGeometry::GetStaticNameOfClass());
auto metaTimePoints = ConvertTimePointListToMetaDataObject(arbitraryTG);
nrrdImageIo->GetMetaDataDictionary().Set(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, metaTimePoints);
}
// Handle properties
mitk::PropertyList::Pointer imagePropertyList = input->GetPropertyList();
for (const auto& property : *imagePropertyList->GetMap())
{
mitk::CoreServicePointer<IPropertyPersistence> propPersistenceService(mitk::CoreServices::GetPropertyPersistence());
IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfo(property.first, GetMimeType()->GetName(), true);
if (infoList.empty())
{
continue;
}
std::string value = infoList.front()->GetSerializationFunction()(property.second);
if (value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING)
{
continue;
}
std::string key = infoList.front()->GetKey();
itk::EncapsulateMetaData<std::string>(nrrdImageIo->GetMetaDataDictionary(), key, value);
}
// Handle UID
itk::EncapsulateMetaData<std::string>(nrrdImageIo->GetMetaDataDictionary(), PROPERTY_KEY_UID, input->GetUID());
ImageReadAccessor imageAccess(inputVector);
nrrdImageIo->Write(imageAccess.GetData());
}
catch (const std::exception &e)
{
mitkThrow() << e.what();
}
// end image write
}
IFileIO::ConfidenceLevel LabelSetImageIO::GetReaderConfidenceLevel() const
{
if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported)
return Unsupported;
const std::string fileName = this->GetLocalFileName();
itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New();
io->SetFileName(fileName);
io->ReadImageInformation();
itk::MetaDataDictionary imgMetaDataDictionary = io->GetMetaDataDictionary();
std::string value("");
itk::ExposeMetaData<std::string>(imgMetaDataDictionary, "modality", value);
if (value.compare("org.mitk.image.multilabel") == 0)
{
return Supported;
}
else
return Unsupported;
}
std::vector<BaseData::Pointer> LabelSetImageIO::DoRead()
{
mitk::LocaleSwitch localeSwitch("C");
// begin regular image loading, adapted from mitkItkImageIO
itk::NrrdImageIO::Pointer nrrdImageIO = itk::NrrdImageIO::New();
Image::Pointer image = Image::New();
const unsigned int MINDIM = 2;
const unsigned int MAXDIM = 4;
const std::string path = this->GetLocalFileName();
MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl;
// Check to see if we can read the file given the name or prefix
if (path.empty())
{
mitkThrow() << "Empty filename in mitk::ItkImageIO ";
}
// Got to allocate space for the image. Determine the characteristics of
// the image.
nrrdImageIO->SetFileName(path);
nrrdImageIO->ReadImageInformation();
unsigned int ndim = nrrdImageIO->GetNumberOfDimensions();
if (ndim < MINDIM || ndim > MAXDIM)
{
MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim
<< " dimensions! Reading as 4D.";
ndim = MAXDIM;
}
itk::ImageIORegion ioRegion(ndim);
itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize();
itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex();
unsigned int dimensions[MAXDIM];
dimensions[0] = 0;
dimensions[1] = 0;
dimensions[2] = 0;
dimensions[3] = 0;
ScalarType spacing[MAXDIM];
spacing[0] = 1.0f;
spacing[1] = 1.0f;
spacing[2] = 1.0f;
spacing[3] = 1.0f;
Point3D origin;
origin.Fill(0);
unsigned int i;
for (i = 0; i < ndim; ++i)
{
ioStart[i] = 0;
ioSize[i] = nrrdImageIO->GetDimensions(i);
if (i < MAXDIM)
{
dimensions[i] = nrrdImageIO->GetDimensions(i);
spacing[i] = nrrdImageIO->GetSpacing(i);
if (spacing[i] <= 0)
spacing[i] = 1.0f;
}
if (i < 3)
{
origin[i] = nrrdImageIO->GetOrigin(i);
}
}
ioRegion.SetSize(ioSize);
ioRegion.SetIndex(ioStart);
MITK_INFO << "ioRegion: " << ioRegion << std::endl;
nrrdImageIO->SetIORegion(ioRegion);
void *buffer = new unsigned char[nrrdImageIO->GetImageSizeInBytes()];
nrrdImageIO->Read(buffer);
image->Initialize(MakePixelType(nrrdImageIO), ndim, dimensions);
image->SetImportChannel(buffer, 0, Image::ManageMemory);
// access direction of itk::Image and include spacing
mitk::Matrix3D matrix;
matrix.SetIdentity();
unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim);
for (i = 0; i < itkDimMax3; ++i)
for (j = 0; j < itkDimMax3; ++j)
matrix[i][j] = nrrdImageIO->GetDirection(j)[i];
// re-initialize PlaneGeometry with origin and direction
PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0);
planeGeometry->SetOrigin(origin);
planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix);
// re-initialize SlicedGeometry3D
SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0);
slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2));
slicedGeometry->SetSpacing(spacing);
MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false);
MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true);
// re-initialize TimeGeometry
const itk::MetaDataDictionary& dictionary = nrrdImageIO->GetMetaDataDictionary();
TimeGeometry::Pointer timeGeometry;
if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE))
{ // also check for the name because of backwards compatibility. Past code version stored with the name and not with
// the key
itk::MetaDataObject<std::string>::ConstPointer timeGeometryTypeData;
if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE))
{
timeGeometryTypeData =
dynamic_cast<const itk::MetaDataObject<std::string>*>(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TYPE));
}
else
{
timeGeometryTypeData =
dynamic_cast<const itk::MetaDataObject<std::string>*>(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TYPE));
}
if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass())
{
MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass();
typedef std::vector<TimePointType> TimePointVector;
TimePointVector timePoints;
if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS))
{
timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS));
}
else if (dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS))
{
timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS));
}
if (timePoints.empty())
{
MITK_ERROR << "Stored timepoints are empty. Meta information seems to bee invalid. Switch to ProportionalTimeGeometry fallback";
}
else if (timePoints.size() - 1 != image->GetDimension(3))
{
MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension ("
<< image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback";
}
else
{
ArbitraryTimeGeometry::Pointer arbitraryTimeGeometry = ArbitraryTimeGeometry::New();
TimePointVector::const_iterator pos = timePoints.begin();
auto prePos = pos++;
for (; pos != timePoints.end(); ++prePos, ++pos)
{
arbitraryTimeGeometry->AppendNewTimeStepClone(slicedGeometry, *prePos, *pos);
}
timeGeometry = arbitraryTimeGeometry;
}
}
}
if (timeGeometry.IsNull())
{ // Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry
MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass();
ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New();
propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3));
timeGeometry = propTimeGeometry;
}
image->SetTimeGeometry(timeGeometry);
buffer = nullptr;
MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents();
// end regular image loading
LabelSetImage::Pointer output = ConvertImageToLabelSetImage(image);
// get labels and add them as properties to the image
char keybuffer[256];
unsigned int numberOfLayers = GetIntByKey(dictionary, "layers");
std::string _xmlStr;
mitk::Label::Pointer label;
for (unsigned int layerIdx = 0; layerIdx < numberOfLayers; layerIdx++)
{
sprintf(keybuffer, "layer_%03u", layerIdx);
int numberOfLabels = GetIntByKey(dictionary, keybuffer);
mitk::LabelSet::Pointer labelSet = mitk::LabelSet::New();
for (int labelIdx = 0; labelIdx < numberOfLabels; labelIdx++)
{
tinyxml2::XMLDocument doc;
sprintf(keybuffer, "label_%03u_%05d", layerIdx, labelIdx);
_xmlStr = GetStringByKey(dictionary, keybuffer);
doc.Parse(_xmlStr.c_str(), _xmlStr.size());
auto *labelElem = doc.FirstChildElement("Label");
if (labelElem == nullptr)
mitkThrow() << "Error parsing NRRD header for mitk::LabelSetImage IO";
label = mitk::LabelSetIOHelper::LoadLabelFromXMLDocument(labelElem);
if (label->GetValue() == 0) // set exterior label is needed to hold exterior information
output->SetExteriorLabel(label);
labelSet->AddLabel(label);
labelSet->SetLayer(layerIdx);
}
output->AddLabelSetToLayer(layerIdx, labelSet);
}
for (auto iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd;
++iter)
{
if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string))
{
const std::string& key = iter->first;
std::string assumedPropertyName = key;
std::replace(assumedPropertyName.begin(), assumedPropertyName.end(), '_', '.');
std::string mimeTypeName = GetMimeType()->GetName();
// Check if there is already a info for the key and our mime type.
mitk::CoreServicePointer<IPropertyPersistence> propPersistenceService(mitk::CoreServices::GetPropertyPersistence());
IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfoByKey(key);
auto predicate = [&mimeTypeName](const PropertyPersistenceInfo::ConstPointer& x) {
return x.IsNotNull() && x->GetMimeTypeName() == mimeTypeName;
};
auto finding = std::find_if(infoList.begin(), infoList.end(), predicate);
if (finding == infoList.end())
{
auto predicateWild = [](const PropertyPersistenceInfo::ConstPointer& x) {
return x.IsNotNull() && x->GetMimeTypeName() == PropertyPersistenceInfo::ANY_MIMETYPE_NAME();
};
finding = std::find_if(infoList.begin(), infoList.end(), predicateWild);
}
PropertyPersistenceInfo::ConstPointer info;
if (finding != infoList.end())
{
assumedPropertyName = (*finding)->GetName();
info = *finding;
}
else
{ // we have not found anything suitable so we generate our own info
auto newInfo = PropertyPersistenceInfo::New();
newInfo->SetNameAndKey(assumedPropertyName, key);
newInfo->SetMimeTypeName(PropertyPersistenceInfo::ANY_MIMETYPE_NAME());
info = newInfo;
}
std::string value =
dynamic_cast<itk::MetaDataObject<std::string>*>(iter->second.GetPointer())->GetMetaDataObjectValue();
mitk::BaseProperty::Pointer loadedProp = info->GetDeserializationFunction()(value);
if (loadedProp.IsNull())
{
MITK_ERROR << "Property cannot be correctly deserialized and is skipped. Check if data format is valid. Problematic property value string: \"" << value << "\"; Property info used to deserialized: " << info;
break;
}
output->SetProperty(assumedPropertyName.c_str(), loadedProp);
// Read properties should be persisted unless they are default properties
// which are written anyway
bool isDefaultKey = false;
for (const auto& defaultKey : m_DefaultMetaDataKeys)
{
if (defaultKey.length() <= assumedPropertyName.length())
{
// does the start match the default key
if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos)
{
isDefaultKey = true;
break;
}
}
}
if (!isDefaultKey)
{
propPersistenceService->AddInfo(info);
}
}
}
// Handle UID
if (dictionary.HasKey(PROPERTY_KEY_UID))
{
itk::MetaDataObject<std::string>::ConstPointer uidData = dynamic_cast<const itk::MetaDataObject<std::string>*>(dictionary.Get(PROPERTY_KEY_UID));
if (uidData.IsNotNull())
{
mitk::UIDManipulator uidManipulator(output);
uidManipulator.SetUID(uidData->GetMetaDataObjectValue());
}
}
MITK_INFO << "...finished!";
std::vector<BaseData::Pointer> result;
result.push_back(output.GetPointer());
return result;
}
int LabelSetImageIO::GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str)
{
std::vector<std::string> imgMetaKeys = dic.GetKeys();
std::vector<std::string>::const_iterator itKey = imgMetaKeys.begin();
std::string metaString("");
for (; itKey != imgMetaKeys.end(); itKey++)
{
itk::ExposeMetaData<std::string>(dic, *itKey, metaString);
if (itKey->find(str.c_str()) != std::string::npos)
{
return atoi(metaString.c_str());
}
}
return 0;
}
std::string LabelSetImageIO::GetStringByKey(const itk::MetaDataDictionary &dic, const std::string &str)
{
std::vector<std::string> imgMetaKeys = dic.GetKeys();
std::vector<std::string>::const_iterator itKey = imgMetaKeys.begin();
std::string metaString("");
for (; itKey != imgMetaKeys.end(); itKey++)
{
itk::ExposeMetaData<std::string>(dic, *itKey, metaString);
if (itKey->find(str.c_str()) != std::string::npos)
{
return metaString;
}
}
return metaString;
}
LabelSetImageIO *LabelSetImageIO::IOClone() const { return new LabelSetImageIO(*this); }
void LabelSetImageIO::InitializeDefaultMetaDataKeys()
{
this->m_DefaultMetaDataKeys.push_back("NRRD.space");
this->m_DefaultMetaDataKeys.push_back("NRRD.kinds");
this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE);
this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS);
this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName");
this->m_DefaultMetaDataKeys.push_back("label_");
this->m_DefaultMetaDataKeys.push_back("layer_");
}
} // namespace
#endif //__mitkLabelSetImageWriter__cpp
diff --git a/Modules/Multilabel/files.cmake b/Modules/Multilabel/files.cmake
index bde04e5e6f..6f267f401b 100644
--- a/Modules/Multilabel/files.cmake
+++ b/Modules/Multilabel/files.cmake
@@ -1,19 +1,20 @@
set(CPP_FILES
mitkLabel.cpp
mitkLabelSet.cpp
mitkLabelSetImage.cpp
mitkLabelSetImageConverter.cpp
mitkLabelSetImageSource.cpp
+ mitkLabelSetImageHelper.cpp
mitkLabelSetImageSurfaceStampFilter.cpp
mitkLabelSetImageToSurfaceFilter.cpp
mitkLabelSetImageToSurfaceThreadedFilter.cpp
mitkLabelSetImageVtkMapper2D.cpp
mitkMultilabelObjectFactory.cpp
mitkLabelSetIOHelper.cpp
mitkDICOMSegmentationPropertyHelper.cpp
mitkDICOMSegmentationConstants.cpp
)
set(RESOURCE_FILES
)
diff --git a/Modules/Multilabel/mitkLabelSetImageConverter.cpp b/Modules/Multilabel/mitkLabelSetImageConverter.cpp
index 15868c5d6d..8486db5c76 100644
--- a/Modules/Multilabel/mitkLabelSetImageConverter.cpp
+++ b/Modules/Multilabel/mitkLabelSetImageConverter.cpp
@@ -1,145 +1,145 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <mitkITKImageImport.h>
#include <mitkImageAccessByItk.h>
#include <mitkImageCast.h>
#include <mitkLabelSetImageConverter.h>
#include <itkComposeImageFilter.h>
#include <itkExtractImageFilter.h>
#include <itkImageDuplicator.h>
#include <itkVectorIndexSelectionCastImageFilter.h>
template <typename TPixel, unsigned int VDimension>
static void ConvertLabelSetImageToImage(const itk::Image<TPixel, VDimension> *,
mitk::LabelSetImage::ConstPointer labelSetImage,
mitk::Image::Pointer &image)
{
typedef itk::Image<TPixel, VDimension> ImageType;
typedef itk::ComposeImageFilter<ImageType> ComposeFilterType;
typedef itk::ImageDuplicator<ImageType> DuplicatorType;
auto numberOfLayers = labelSetImage->GetNumberOfLayers();
if (numberOfLayers > 1)
{
auto vectorImageComposer = ComposeFilterType::New();
auto activeLayer = labelSetImage->GetActiveLayer();
for (decltype(numberOfLayers) layer = 0; layer < numberOfLayers; ++layer)
{
auto layerImage = mitk::ImageToItkImage<TPixel, VDimension>(
layer != activeLayer ? labelSetImage->GetLayerImage(layer) : labelSetImage);
vectorImageComposer->SetInput(layer, layerImage);
}
vectorImageComposer->Update();
// mitk::GrabItkImageMemory does not support 4D, this will handle 4D correctly
// and create a memory managed copy
image = mitk::ImportItkImage(vectorImageComposer->GetOutput())->Clone();
}
else
{
auto layerImage = mitk::ImageToItkImage<TPixel, VDimension>(labelSetImage);
auto duplicator = DuplicatorType::New();
duplicator->SetInputImage(layerImage);
duplicator->Update();
// mitk::GrabItkImageMemory does not support 4D, this will handle 4D correctly
// and create a memory managed copy
image = mitk::ImportItkImage(duplicator->GetOutput())->Clone();
}
}
mitk::Image::Pointer mitk::ConvertLabelSetImageToImage(LabelSetImage::ConstPointer labelSetImage)
{
Image::Pointer image;
if (labelSetImage->GetNumberOfLayers() > 0)
{
if (labelSetImage->GetDimension() == 4)
{
AccessFixedDimensionByItk_n(labelSetImage, ::ConvertLabelSetImageToImage, 4, (labelSetImage, image));
}
else
{
AccessByItk_2(labelSetImage->GetLayerImage(0), ::ConvertLabelSetImageToImage, labelSetImage, image);
}
image->SetTimeGeometry(labelSetImage->GetTimeGeometry()->Clone());
}
return image;
}
template <typename TPixel, unsigned int VDimensions>
static void ConvertImageToLabelSetImage(const itk::VectorImage<TPixel, VDimensions> *image,
mitk::LabelSetImage::Pointer &labelSetImage)
{
typedef itk::VectorImage<TPixel, VDimensions> VectorImageType;
typedef itk::Image<TPixel, VDimensions> ImageType;
typedef itk::VectorIndexSelectionCastImageFilter<VectorImageType, ImageType> VectorIndexSelectorType;
labelSetImage = mitk::LabelSetImage::New();
auto numberOfLayers = image->GetVectorLength();
for (decltype(numberOfLayers) layer = 0; layer < numberOfLayers; ++layer)
{
auto layerSelector = VectorIndexSelectorType::New();
layerSelector->SetInput(image);
layerSelector->SetIndex(layer);
layerSelector->Update();
mitk::Image::Pointer layerImage;
mitk::CastToMitkImage(layerSelector->GetOutput(), layerImage);
if (layer == 0)
{
labelSetImage->InitializeByLabeledImage(layerImage);
}
else
{
labelSetImage->AddLayer(layerImage);
}
}
}
mitk::LabelSetImage::Pointer mitk::ConvertImageToLabelSetImage(Image::Pointer image)
{
LabelSetImage::Pointer labelSetImage;
if (image.IsNotNull())
{
- if (image->GetChannelDescriptor().GetPixelType().GetPixelType() == itk::ImageIOBase::VECTOR)
+ if (image->GetChannelDescriptor().GetPixelType().GetPixelType() == itk::IOPixelEnum::VECTOR)
{
if (4 == image->GetDimension())
{
AccessVectorFixedDimensionByItk_n(image, ::ConvertImageToLabelSetImage, 4, (labelSetImage));
}
else
{
AccessVectorPixelTypeByItk_n(image, ::ConvertImageToLabelSetImage, (labelSetImage));
}
}
else
{
labelSetImage = mitk::LabelSetImage::New();
labelSetImage->InitializeByLabeledImage(image);
}
labelSetImage->SetTimeGeometry(image->GetTimeGeometry()->Clone());
}
return labelSetImage;
}
diff --git a/Modules/Multilabel/mitkLabelSetImageHelper.cpp b/Modules/Multilabel/mitkLabelSetImageHelper.cpp
new file mode 100644
index 0000000000..8b4eaed591
--- /dev/null
+++ b/Modules/Multilabel/mitkLabelSetImageHelper.cpp
@@ -0,0 +1,77 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#include <mitkLabelSetImageHelper.h>
+
+#include <mitkLabelSetImage.h>
+#include <mitkExceptionMacro.h>
+#include <mitkProperties.h>
+
+mitk::DataNode::Pointer mitk::LabelSetImageHelper::CreateNewSegmentationNode(const DataNode* referenceNode,
+ const Image* initialSegmentationImage, const std::string& segmentationName)
+{
+ std::string newSegmentationName = segmentationName;
+ if (newSegmentationName.empty())
+ {
+ newSegmentationName = referenceNode->GetName();
+ newSegmentationName.append("-labels");
+ }
+
+ if (nullptr == initialSegmentationImage)
+ {
+ return nullptr;
+ }
+
+ auto newLabelSetImage = mitk::LabelSetImage::New();
+ try
+ {
+ newLabelSetImage->Initialize(initialSegmentationImage);
+ }
+ catch (mitk::Exception &e)
+ {
+ mitkReThrow(e) << "Could not initialize new label set image.";
+ return nullptr;
+ }
+
+ auto newSegmentationNode = mitk::DataNode::New();
+ newSegmentationNode->SetData(newLabelSetImage);
+ newSegmentationNode->SetName(newSegmentationName);
+
+ // set additional image information
+ newLabelSetImage->GetExteriorLabel()->SetProperty("name.parent", mitk::StringProperty::New(referenceNode->GetName()));
+ newLabelSetImage->GetExteriorLabel()->SetProperty("name.image", mitk::StringProperty::New(newSegmentationName));
+
+ // initialize "showVolume"-property to false to prevent recalculating the volume while working on the segmentation
+ newSegmentationNode->SetProperty("showVolume", mitk::BoolProperty::New(false));
+
+ return newSegmentationNode;
+}
+
+mitk::Label::Pointer mitk::LabelSetImageHelper::CreateNewLabel(const LabelSetImage* labelSetImage)
+{
+ int numberOfLabels = labelSetImage->GetActiveLabelSet()->GetNumberOfLabels();
+
+ std::string labelName = "New label " + std::to_string(numberOfLabels);
+
+ mitk::LookupTable::Pointer lookupTable = mitk::LookupTable::New();
+ lookupTable->SetType(mitk::LookupTable::LookupTableType::MULTILABEL);
+ double rgb[3];
+ lookupTable->GetColor(numberOfLabels, rgb);
+ mitk::Color labelColor;
+ labelColor.Set(static_cast<float>(rgb[0]), static_cast<float>(rgb[1]), static_cast<float>(rgb[2]));
+
+ mitk::Label::Pointer newLabel = mitk::Label::New();
+ newLabel->SetName(labelName);
+ newLabel->SetColor(labelColor);
+
+ return newLabel;
+}
diff --git a/Modules/Multilabel/mitkLabelSetImageHelper.h b/Modules/Multilabel/mitkLabelSetImageHelper.h
new file mode 100644
index 0000000000..44c6fc4187
--- /dev/null
+++ b/Modules/Multilabel/mitkLabelSetImageHelper.h
@@ -0,0 +1,60 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#ifndef MITKLABELSETIMAGEHELPER_H
+#define MITKLABELSETIMAGEHELPER_H
+
+#include <MitkMultilabelExports.h>
+
+#include <mitkDataNode.h>
+#include <mitkLabelSetImage.h>
+
+namespace mitk
+{
+ /**
+ *
+ */
+ namespace LabelSetImageHelper
+ {
+ /**
+ * @brief This function creates and returns a new data node containing the given image as data.
+ * The segmentation node is named according to the given reference data node.
+ * Some properties are set to automatically link the segmentation node with its parent node.
+ *
+ * @param referenceNode The reference node from which the name of the new segmentation node
+ * is derived.
+ * @param initialSegmentationImage The segmentation image that is used to initialize the label set image.
+ * @param segmentationName An optional name for the new segmentation node.
+ *
+ * @return The new segmentation node as a data node pointer.
+ */
+ MITKMULTILABEL_EXPORT mitk::DataNode::Pointer CreateNewSegmentationNode(const DataNode* referenceNode,
+ const Image* initialSegmentationImage = nullptr, const std::string& segmentationName = std::string());
+
+ /**
+ * @brief This function creates and returns a new label. The label is automatically assigned a
+ * label name, depending on the current number of labels of the active label set of the
+ * given label set image.
+ * The color of the label is derived from the MULTILABEL lookup table, depending on the
+ * number of labels.
+ *
+ * @param labelSetImage The label set image from which the number of labels of the active label
+ * set is derived.
+ *
+ * @return The new labe as a pointer.
+ */
+ MITKMULTILABEL_EXPORT mitk::Label::Pointer CreateNewLabel(const LabelSetImage* labelSetImage);
+
+ } // namespace LabelSetImageHelper
+} // namespace mitk
+
+#endif // MITKLABELSETIMAGEHELPER_H
diff --git a/Modules/OpenCVVideoSupport/Commands/mitkGrabCutOpenCVImageFilter.cpp b/Modules/OpenCVVideoSupport/Commands/mitkGrabCutOpenCVImageFilter.cpp
index 356659fe52..294c802013 100644
--- a/Modules/OpenCVVideoSupport/Commands/mitkGrabCutOpenCVImageFilter.cpp
+++ b/Modules/OpenCVVideoSupport/Commands/mitkGrabCutOpenCVImageFilter.cpp
@@ -1,412 +1,391 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// mitk headers
#include "mitkGrabCutOpenCVImageFilter.h"
#include "mitkPointSet.h"
-// itk headers
-#include "itkMultiThreader.h"
-#include "itkFastMutexLock.h"
-#include "itkConditionVariable.h"
-
#include <opencv2/imgproc.hpp>
// This is a magic number defined in "grabcut.cpp" of OpenCV.
// GrabCut function crashes if less than this number of model
// points are given. There must be at least as much model points
// as components of the Gaussian Mixture Model.
#define GMM_COMPONENTS_COUNT 5
mitk::GrabCutOpenCVImageFilter::GrabCutOpenCVImageFilter()
: m_ModelPointsDilationSize(0),
m_UseOnlyRegionAroundModelPoints(false),
m_CurrentProcessImageNum(0),
m_InputImageId(AbstractOpenCVImageFilter::INVALID_IMAGE_ID),
m_ResultImageId(AbstractOpenCVImageFilter::INVALID_IMAGE_ID),
- m_ThreadId(-1), m_StopThread(false),
- m_MultiThreader(itk::MultiThreader::New()),
- m_WorkerBarrier(itk::ConditionVariable::New()),
- m_ImageMutex(itk::FastMutexLock::New()),
- m_ResultMutex(itk::FastMutexLock::New()),
- m_PointSetsMutex(itk::FastMutexLock::New())
+ m_StopThread(false)
{
- m_ThreadId = m_MultiThreader->SpawnThread(this->SegmentationWorker, this);
+ m_Thread = std::thread(&GrabCutOpenCVImageFilter::SegmentationWorker, this);
}
mitk::GrabCutOpenCVImageFilter::~GrabCutOpenCVImageFilter()
{
// terminate worker thread on destruction
m_StopThread = true;
- m_WorkerBarrier->Broadcast();
- if ( m_ThreadId >= 0) { m_MultiThreader->TerminateThread(m_ThreadId); }
+ m_WorkerBarrier.notify_all();
+ if (m_Thread.joinable())
+ m_Thread.detach();
}
bool mitk::GrabCutOpenCVImageFilter::OnFilterImage( cv::Mat& image )
{
if ( image.empty() )
{
MITK_WARN << "Filtering empty image?";
return false;
}
// make sure that the image is an rgb image as needed
// by the GrabCut algorithm
if (image.type() != CV_8UC3)
{
cv::Mat tmp = image.clone();
cv::cvtColor(tmp, image, CV_GRAY2RGB);
}
// set image as the current input image, guarded by
// a mutex as the worker thread reads this imagr
- m_ImageMutex->Lock();
+ m_ImageMutex.lock();
m_InputImage = image.clone();
m_InputImageId = this->GetCurrentImageId();
- m_ImageMutex->Unlock();
+ m_ImageMutex.unlock();
// wake up the worker thread if there was an image set
// and foreground model points are available
- if ( ! m_ForegroundPoints.empty()) { m_WorkerBarrier->Broadcast(); }
+ if ( ! m_ForegroundPoints.empty()) { m_WorkerBarrier.notify_all(); }
return true;
}
void mitk::GrabCutOpenCVImageFilter::SetModelPoints(ModelPointsList foregroundPoints)
{
- m_PointSetsMutex->Lock();
+ m_PointSetsMutex.lock();
m_ForegroundPoints = foregroundPoints;
- m_PointSetsMutex->Unlock();
+ m_PointSetsMutex.unlock();
}
void mitk::GrabCutOpenCVImageFilter::SetModelPoints(ModelPointsList foregroundPoints, ModelPointsList backgroundPoints)
{
- m_PointSetsMutex->Lock();
+ m_PointSetsMutex.lock();
m_BackgroundPoints = backgroundPoints;
m_ForegroundPoints = foregroundPoints;
- m_PointSetsMutex->Unlock();
+ m_PointSetsMutex.unlock();
}
void mitk::GrabCutOpenCVImageFilter::SetModelPoints(cv::Mat foregroundMask)
{
- m_PointSetsMutex->Lock();
+ m_PointSetsMutex.lock();
m_ForegroundPoints = this->ConvertMaskToModelPointsList(foregroundMask);
- m_PointSetsMutex->Unlock();
+ m_PointSetsMutex.unlock();
}
void mitk::GrabCutOpenCVImageFilter::SetModelPoints(cv::Mat foregroundMask, cv::Mat backgroundMask)
{
- m_PointSetsMutex->Lock();
+ m_PointSetsMutex.lock();
m_ForegroundPoints = this->ConvertMaskToModelPointsList(foregroundMask);
m_BackgroundPoints = this->ConvertMaskToModelPointsList(backgroundMask);
- m_PointSetsMutex->Unlock();
+ m_PointSetsMutex.unlock();
}
void mitk::GrabCutOpenCVImageFilter::SetModelPointsDilationSize(int modelPointsDilationSize)
{
if ( modelPointsDilationSize < 0 )
{
MITK_ERROR("AbstractOpenCVImageFilter")("GrabCutOpenCVImageFilter")
<< "Model points dilation size must not be smaller then zero.";
mitkThrow() << "Model points dilation size must not be smaller then zero.";
}
m_ModelPointsDilationSize = modelPointsDilationSize;
}
void mitk::GrabCutOpenCVImageFilter::SetUseOnlyRegionAroundModelPoints(unsigned int additionalWidth)
{
m_UseOnlyRegionAroundModelPoints = true;
m_AdditionalWidth = additionalWidth;
}
void mitk::GrabCutOpenCVImageFilter::SetUseFullImage()
{
m_UseOnlyRegionAroundModelPoints = false;
}
cv::Rect mitk::GrabCutOpenCVImageFilter::GetRegionAroundModelPoints()
{
return m_BoundingBox;
}
int mitk::GrabCutOpenCVImageFilter::GetResultImageId()
{
return m_ResultImageId;
}
cv::Mat mitk::GrabCutOpenCVImageFilter::GetResultMask()
{
cv::Mat result;
- m_ResultMutex->Lock();
+ m_ResultMutex.lock();
result = m_ResultMask.clone();
- m_ResultMutex->Unlock();
+ m_ResultMutex.unlock();
return result;
}
std::vector<mitk::GrabCutOpenCVImageFilter::ModelPointsList> mitk::GrabCutOpenCVImageFilter::GetResultContours()
{
std::vector<std::vector<cv::Point> > cvContours;
std::vector<cv::Vec4i> hierarchy;
std::vector<mitk::GrabCutOpenCVImageFilter::ModelPointsList> contourPoints;
cv::Mat resultMask = this->GetResultMask();
if (resultMask.empty()) { return contourPoints; }
cv::findContours(resultMask, cvContours, hierarchy, cv::RETR_LIST, cv::CHAIN_APPROX_NONE);
// convert cvContours to vector of ModelPointsLists
for ( unsigned int i = 0; i < cvContours.size(); ++i )
{
mitk::GrabCutOpenCVImageFilter::ModelPointsList curContourPoints;
for ( auto it = cvContours[i].begin();
it != cvContours[i].end(); ++it)
{
itk::Index<2> index;
index.SetElement(0, it->x);
index.SetElement(1, it->y);
curContourPoints.push_back(index);
}
contourPoints.push_back(curContourPoints);
}
return contourPoints;
}
mitk::GrabCutOpenCVImageFilter::ModelPointsList mitk::GrabCutOpenCVImageFilter::GetResultContourWithPixel(itk::Index<2> pixelIndex)
{
cv::Mat mask = this->GetResultMask();
if (mask.empty()) { return mitk::GrabCutOpenCVImageFilter::ModelPointsList(); }
// return empty model point list if given pixel is outside the image borders
if (pixelIndex.GetElement(0) < 0 || pixelIndex.GetElement(0) >= mask.size().height
|| pixelIndex.GetElement(1) < 0 || pixelIndex.GetElement(1) >= mask.size().width)
{
MITK_WARN("AbstractOpenCVImageFilter")("GrabCutOpenCVImageFilter")
<< "Given pixel index ("<< pixelIndex.GetElement(0) << ", " << pixelIndex.GetElement(1)
<< ") is outside the image (" << mask.size().height << ", " << mask.size().width << ").";
return mitk::GrabCutOpenCVImageFilter::ModelPointsList();
}
// create a mask where the segmentation around the given pixel index is
// set (done by flood filling the result mask using the pixel as seed)
cv::floodFill(mask, cv::Point(pixelIndex.GetElement(0), pixelIndex.GetElement(1)), 5);
cv::Mat foregroundMask;
cv::compare(mask, 5, foregroundMask, cv::CMP_EQ);
// find the contour on the flood filled image (there can be only one now)
std::vector<std::vector<cv::Point> > cvContours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(foregroundMask, cvContours, hierarchy, cv::RETR_LIST, cv::CHAIN_APPROX_NONE);
ModelPointsList contourPoints;
// convert cvContours to ModelPointsList
for ( auto it = cvContours[0].begin();
it != cvContours[0].end(); ++it)
{
itk::Index<2> index;
index.SetElement(0, it->x);
index.SetElement(1, it->y);
contourPoints.push_back(index);
}
return contourPoints;
}
cv::Mat mitk::GrabCutOpenCVImageFilter::GetMaskFromPointSets()
{
// initialize mask with values of propably background
cv::Mat mask(m_InputImage.size().height, m_InputImage.size().width, CV_8UC1, cv::GC_PR_BGD);
// get foreground and background points (guarded by mutex)
- m_PointSetsMutex->Lock();
+ m_PointSetsMutex.lock();
ModelPointsList pointsLists[2] = {ModelPointsList(m_ForegroundPoints), ModelPointsList(m_BackgroundPoints)};
- m_PointSetsMutex->Unlock();
+ m_PointSetsMutex.unlock();
// define values for foreground and background pixels
unsigned int pixelValues[2] = {cv::GC_FGD, cv::GC_BGD};
for (unsigned int n = 0; n < 2; ++n)
{
for (auto it = pointsLists[n].begin();
it != pointsLists[n].end(); ++it)
{
// set pixels around current pixel to the same value (size of this
// area is specified by ModelPointsDilationSize)
for ( int i = -m_ModelPointsDilationSize; i <= m_ModelPointsDilationSize; ++i )
{
for ( int j = -m_ModelPointsDilationSize; j <= m_ModelPointsDilationSize; ++j)
{
int x = it->GetElement(1) + i; int y = it->GetElement(0) + j;
if ( x >= 0 && y >= 0 && x < mask.cols && y < mask.rows)
{
mask.at<unsigned char>(x, y) = pixelValues[n];
}
}
}
}
}
return mask;
}
cv::Rect mitk::GrabCutOpenCVImageFilter::GetBoundingRectFromMask(cv::Mat mask)
{
cv::Mat nonPropablyBackgroundMask, modelPoints;
cv::compare(mask, cv::GC_PR_BGD, nonPropablyBackgroundMask, cv::CMP_NE);
cv::findNonZero(nonPropablyBackgroundMask, modelPoints);
if (modelPoints.empty())
{
MITK_WARN("AbstractOpenCVImageFilter")("GrabCutOpenCVImageFilter")
<< "Cannot find any foreground points. Returning full image size as bounding rectangle.";
return cv::Rect(0, 0, mask.rows, mask.cols);
}
// calculate bounding rect around the model points
cv::Rect boundingRect = cv::boundingRect(modelPoints);
// substract additional width to x and y value (and make sure that they aren't outside the image then)
boundingRect.x = static_cast<unsigned int>(boundingRect.x) > m_AdditionalWidth ? boundingRect.x - m_AdditionalWidth : 0;
boundingRect.y = static_cast<unsigned int>(boundingRect.y) > m_AdditionalWidth ? boundingRect.y - m_AdditionalWidth : 0;
// add additional width to width of bounding rect (twice as x value was moved before)
// and make sure that the bounding rect will stay inside the image borders)
if ( static_cast<unsigned int>(boundingRect.x + boundingRect.width)
+ 2 * m_AdditionalWidth < static_cast<unsigned int>(mask.size().width) )
{
boundingRect.width += 2 * m_AdditionalWidth;
}
else
{
boundingRect.width = mask.size().width - boundingRect.x - 1;
}
// add additional width to height of bounding rect (twice as y value was moved before)
// and make sure that the bounding rect will stay inside the image borders)
if ( static_cast<unsigned int>(boundingRect.y + boundingRect.height)
+ 2 * m_AdditionalWidth < static_cast<unsigned int>(mask.size().height) )
{
boundingRect.height += 2 * m_AdditionalWidth;
}
else
{
boundingRect.height = mask.size().height - boundingRect.y - 1;
}
assert(boundingRect.x + boundingRect.width < mask.size().width);
assert(boundingRect.y + boundingRect.height < mask.size().height);
return boundingRect;
}
cv::Mat mitk::GrabCutOpenCVImageFilter::RunSegmentation(cv::Mat input, cv::Mat mask)
{
// test if foreground and background models are large enough for GrabCut
cv::Mat compareFgResult, compareBgResult;
cv::compare(mask, cv::GC_FGD, compareFgResult, cv::CMP_EQ);
cv::compare(mask, cv::GC_PR_BGD, compareBgResult, cv::CMP_EQ);
if ( cv::countNonZero(compareFgResult) < GMM_COMPONENTS_COUNT
|| cv::countNonZero(compareBgResult) < GMM_COMPONENTS_COUNT)
{
// return result mask with no pixels set to foreground
return cv::Mat::zeros(mask.size(), mask.type());
}
// do the actual grab cut segmentation (initialized with the mask)
cv::Mat bgdModel, fgdModel;
cv::grabCut(input, mask, cv::Rect(), bgdModel, fgdModel, 1, cv::GC_INIT_WITH_MASK);
// set propably foreground pixels to white on result mask
cv::Mat result;
cv::compare(mask, cv::GC_PR_FGD, result, cv::CMP_EQ);
// set foreground pixels to white on result mask
cv::Mat foregroundMat;
cv::compare(mask, cv::GC_FGD, foregroundMat, cv::CMP_EQ);
foregroundMat.copyTo(result, foregroundMat);
return result; // now the result mask can be returned
}
mitk::GrabCutOpenCVImageFilter::ModelPointsList mitk::GrabCutOpenCVImageFilter::ConvertMaskToModelPointsList(cv::Mat mask)
{
cv::Mat points;
cv::findNonZero(mask, points);
// push extracted points into a vector of itk indices
ModelPointsList pointsVector;
for ( size_t n = 0; n < points.total(); ++n)
{
itk::Index<2> index;
index.SetElement(0, points.at<cv::Point>(n).x);
index.SetElement(1, points.at<cv::Point>(n).y);
pointsVector.push_back(index);
}
return pointsVector;
}
-ITK_THREAD_RETURN_TYPE mitk::GrabCutOpenCVImageFilter::SegmentationWorker(void* pInfoStruct)
+void mitk::GrabCutOpenCVImageFilter::SegmentationWorker()
{
- // extract this pointer from thread info structure
- struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct;
- mitk::GrabCutOpenCVImageFilter* thisObject = static_cast<mitk::GrabCutOpenCVImageFilter*>(pInfo->UserData);
-
- itk::SimpleMutexLock mutex;
- mutex.Lock();
+ std::mutex mutex;
+ std::unique_lock<std::mutex> lock(mutex);
while (true)
{
- if (thisObject->m_StopThread) { break; }
-
- thisObject->m_WorkerBarrier->Wait(&mutex);
+ m_WorkerBarrier.wait(lock, [this] { return !m_StopThread; });
- if (thisObject->m_StopThread) { break; }
+ m_ImageMutex.lock();
+ cv::Mat image = m_InputImage.clone();
+ int inputImageId = m_InputImageId;
+ m_ImageMutex.unlock();
- thisObject->m_ImageMutex->Lock();
- cv::Mat image = thisObject->m_InputImage.clone();
- int inputImageId = thisObject->m_InputImageId;
- thisObject->m_ImageMutex->Unlock();
-
- cv::Mat mask = thisObject->GetMaskFromPointSets();
+ cv::Mat mask = this->GetMaskFromPointSets();
cv::Mat result;
- if (thisObject->m_UseOnlyRegionAroundModelPoints)
+ if (m_UseOnlyRegionAroundModelPoints)
{
result = cv::Mat(mask.rows, mask.cols, mask.type(), 0.0);
- thisObject->m_BoundingBox = thisObject->GetBoundingRectFromMask(mask);
- thisObject->RunSegmentation(image(thisObject->m_BoundingBox), mask(thisObject->m_BoundingBox)).copyTo(result(thisObject->m_BoundingBox));
+ m_BoundingBox = this->GetBoundingRectFromMask(mask);
+ RunSegmentation(image(m_BoundingBox), mask(m_BoundingBox)).copyTo(result(m_BoundingBox));
}
else
{
- result = thisObject->RunSegmentation(image, mask);
+ result = this->RunSegmentation(image, mask);
}
// save result to member attribute
- thisObject->m_ResultMutex->Lock();
- thisObject->m_ResultMask = result;
- thisObject->m_ResultImageId = inputImageId;
- thisObject->m_ResultMutex->Unlock();
+ m_ResultMutex.lock();
+ m_ResultMask = result;
+ m_ResultImageId = inputImageId;
+ m_ResultMutex.unlock();
}
-
- mutex.Unlock();
-
- return ITK_THREAD_RETURN_VALUE;
}
diff --git a/Modules/OpenCVVideoSupport/Commands/mitkGrabCutOpenCVImageFilter.h b/Modules/OpenCVVideoSupport/Commands/mitkGrabCutOpenCVImageFilter.h
index 361c0dad68..9cd859477a 100644
--- a/Modules/OpenCVVideoSupport/Commands/mitkGrabCutOpenCVImageFilter.h
+++ b/Modules/OpenCVVideoSupport/Commands/mitkGrabCutOpenCVImageFilter.h
@@ -1,287 +1,289 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKGRABCUTOPENCVIMAGEFILTER_H
#define MITKGRABCUTOPENCVIMAGEFILTER_H
// mitk headers
#include "mitkAbstractOpenCVImageFilter.h"
#include "mitkVector.h"
// itk headers
#include "itkObjectFactory.h"
-#include "itkMutexLock.h"
+#include <itkThreadSupport.h>
// opencv headers
#include <opencv2/core.hpp>
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
namespace itk {
template<unsigned int T> class Index;
template<class T> class SmartPointer;
class MultiThreader;
class ConditionVariable;
class FastMutexLock;
}
namespace mitk {
class PointSet;
/**
* \brief Makes the OpenCV GrabCut filter available as OpenCVImageFilter.
*
* Image filtering is done asynchronly by using a worker thread as GrabCut segmentation
* can take up to some seconds. Calling the mitk::GrabCutOpenCVImageFilter::OnFilterImage()
* method sets just the input image and wakes up the worker thread. It is not guaranteed
* that every image gets segmented. If multiple new images where set before a segmentation
* was finished, only the last new image gets segmented afterwards.
*
* At least foreground model points have to be set by
* mitk::GrabCutOpenCVImageFilter::SetModelPoints() before a segmentation can be performed.
* The worder thread will not be waken up before any model points were set.
*
* When a new segmentation is ready, mitk::GrabCutOpenCVImageFilter::GetCurrentImageId()
* returns a new image id. The segmentation can be got then by calling
* mitk::GrabCutOpenCVImageFilter::GetResultMask(),
* mitk::GrabCutOpenCVImageFilter::GetResultContours() or
* mitk::GrabCutOpenCVImageFilter::GetResultContourWithPixel().
*/
class MITKOPENCVVIDEOSUPPORT_EXPORT GrabCutOpenCVImageFilter : public AbstractOpenCVImageFilter
{
public:
/** \brief List holding image indices of the model points. */
typedef std::vector<itk::Index<2> > ModelPointsList;
mitkClassMacro(GrabCutOpenCVImageFilter, AbstractOpenCVImageFilter);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GrabCutOpenCVImageFilter();
~GrabCutOpenCVImageFilter() override;
/**
* \brief Implementation of the virtual image filtering method.
* The input image is copied to a member attribute, but the actual filtering is not done
* in this method. Instead a worker thread is waken up every time this method is called,
* if foreground model points were set before.
*
* \param image OpenCV image to be segmentated
* \return false if an empty image was set, true otherwise
*/
bool OnFilterImage( cv::Mat& image ) override;
/**
* \brief Sets a list of image indices as foreground model points.
* \param foregroundPoints List of image indices which definitely belong to the foreground.
*/
void SetModelPoints(ModelPointsList foregroundPoints);
/**
* \brief Sets a list of image indices as foreground and another list as background model points.
* \param foregroundPoints List of image indices which definitely belong to the foreground.
* \param backgroundPoints List of image indices which definitely belong to the background.
*/
void SetModelPoints(ModelPointsList foregroundPoints, ModelPointsList backgroundPoints);
/**
* \brief Sets a mask where every non-zero pixel is treated as a foreground model pixel.
*/
void SetModelPoints(cv::Mat foregroundMask);
/**
* \brief Sets masks specifying foreground and background points.
* \param foregroundMask every non-zero pixel is treated as a foreground model pixel
* \param backgroundMask every non-zero pixel is treated as a background model pixel
*/
void SetModelPoints(cv::Mat foregroundMask, cv::Mat backgroundMask);
/**
* \brief Set a size of which each model point is dilated before image filtering.
* The more color information of the foreground object the GrabCut filter gets the better
* the result will be. Therefore the foreground pixels can be dilated before filtering. The
* caller is responsible for setting a dilation size so that no foreground model pixels will
* be indeed part of the background after dilation.
*
* Dilation is done to the background model pixles as well, if there are any set for the
* filter.
*
* \param modelPointsDilationSize how many pixels are added in every direction, 0 sets back to no dilation
*/
void SetModelPointsDilationSize(int modelPointsDilationSize);
/**
* \brief Use only the region around the foreground model points for the segmentation.
*
* This is mainly for reasons of segmentation speed and has the drawback that the foreground
* model points (plus the given additional border) have to cover the whole foreground object.
*
* The segmentation filter can be set back to using the whole image by calling
* mitk::GrabCutOpenCVImageFilter::SetUseFullImage().
*
* \param additionalBorder size of the border around the foreground points which will be used for segmentation, too
*/
void SetUseOnlyRegionAroundModelPoints(unsigned int additionalBorder);
/**
* \brief The full image is used as input for the segmentation.
* This method sets the behaviour back to the default behaviour in case
* mitk::GrabCutOpenCVImageFilter::SetUseOnlyRegionAroundModelPoints() was
* called before.
*/
void SetUseFullImage();
/**
* \brief Getter for the rectangle used for the area of segmentation.
* See mitk::GrabCutOpenCVImageFilter::SetUseOnlyRegionAroundModelPoints().
* This method is mainly for debugging purposes and may be removed in
* the future.
*/
cv::Rect GetRegionAroundModelPoints();
/**
* \brief Getter for an ascending id of the current result image.
* The id will be increased for every segmentation that is produced by the worker thread.
* It can be used to determine if a new segmentation was produced since the last time a
* segmentation was got from this filter.
*
* int lastResultImageId = grabCutFilter->GetResultImageId();
* // do something
* if ( lastResultImageId != grabCutFilter->GetResultImageId() )
* // get new segmentation
*/
int GetResultImageId();
/**
* \brief Getter for the result mask of the current segmentation.
* The result of this method is not necessarily consistent with the result of
* mitk::GrabCutOpenCVImageFilter::GetResultContours() if they are called afterwards.
* The segmentation may have changed in the meantime. One should decide if he needs
* a mask or a contour or convert one into the other on his own.
* \return image of the size of the input image where all pixels segmented as foreground are non-zero
*/
cv::Mat GetResultMask();
/**
* \brief Getter for the contours of the current segmentation.
*
* A segmentation can consist of multiple regions therefore a list of contours
* is returned. If one needs only one specific region he can call
* mitk::GrabCutOpenCVImageFilter::GetResultContourWithPixel().
*
* This result of this method is not necessarily consistent with the result of
* mitk::GrabCutOpenCVImageFilter::GetResultContours() if they are called afterwards.
* The segmentation may have changed in the meantime. One should decide if he needs
* a mask or a contour or convert one into the other on his own.
*
* \return List containing lists of pixel indices for every contour.
*/
std::vector<ModelPointsList> GetResultContours();
/**
* \brief Getter for one specific contour of the current segmentation.
*
* Can be used if only one (of possible multiple contours) is needed. A pixel index
* must be given to select from the contours. This could be one of the foreground
* model pixels for example. If other criteria are needed to distinguish the contours
* mitk::GrabCutOpenCVImageFilter::GetResultContours() can be used instead and therefore
* contour selection can be done by hand then.
*
* This result of this method is not necessarily consistent with the result of
* mitk::GrabCutOpenCVImageFilter::GetResultContours() if they are called afterwards.
* The segmentation may have changed in the meantime. One should decide if he needs
* a mask or a contour or convert one into the other on his own.
*
* \param pixelIndex index of a pixel which lies inside the contour
* \return list of pixel indices for the selected contour
*/
ModelPointsList GetResultContourWithPixel(itk::Index<2> pixelIndex);
protected:
/** \brief Creates an image mask for GrabCut algorithm by using the foreground and background point sets.
* Background and foreground points will be dilated by the size set by
* mitk::GrabCutOpenCVImageFilter::SetModelPointsDilationSize().
*/
cv::Mat GetMaskFromPointSets();
/**
* \brief Creates a bounding box around all pixels which aren't propably background.
* The bounding box is widened as specified by
* mitk::GrabCutOpenCVImageFilter::SetUseOnlyRegionAroundModelPoints().
*/
cv::Rect GetBoundingRectFromMask(cv::Mat mask);
/**
* \brief Performs a GrabCut segmentation of the given input image.
* \param input image on which the segmentation will be performed
* \param mask foreground and background pixels used as basis for segmentation
* \return mask with every pixel of the segmented foreground object set non-zero
*/
cv::Mat RunSegmentation(cv::Mat input, cv::Mat mask);
/**
* \brief Creates a list of points from every non-zero pixel of the given mask.
*/
ModelPointsList ConvertMaskToModelPointsList(cv::Mat mask);
int m_ModelPointsDilationSize;
bool m_UseOnlyRegionAroundModelPoints;
unsigned int m_AdditionalWidth;
cv::Rect m_BoundingBox;
ModelPointsList m_ForegroundPoints;
ModelPointsList m_BackgroundPoints;
cv::Mat m_InputImage;
cv::Mat m_GrabCutMask;
cv::Mat m_ResultMask;
unsigned int m_CurrentProcessImageNum;
/** \brief id of the image currently set as m_InputImage */
int m_InputImageId;
/** \brief id of the image which segmentation result is currently present in m_ResultMask */
int m_ResultImageId;
private:
/**
* \brief Worker thread for doing the segmentation.
* It blocks every time a image was segmented and will be waken up again by
* the mitk::GrabCutOpenCVImageFilter::OnFilterImage() method.
*
- * \param pInfoStruct pointer to the GrabCutOpenCVImageFilter object
* \return
*/
- static ITK_THREAD_RETURN_TYPE SegmentationWorker(void* pInfoStruct);
+ void SegmentationWorker();
- int m_ThreadId;
+ std::thread m_Thread;
/** \brief worker thread will terminate after the next wakeup if set to true */
bool m_StopThread;
- itk::SmartPointer<itk::MultiThreader> m_MultiThreader;
- itk::SmartPointer<itk::ConditionVariable> m_WorkerBarrier;
+ std::condition_variable m_WorkerBarrier;
/** \brief mutex for guarding m_InputImage and m_InputImageId */
- itk::SmartPointer<itk::FastMutexLock> m_ImageMutex;
+ std::mutex m_ImageMutex;
/** \brief mutex for guarding m_ResultMask and m_ResultImageId */
- itk::SmartPointer<itk::FastMutexLock> m_ResultMutex;
+ std::mutex m_ResultMutex;
/** \brief mutex for guarding m_ForegroundPoints and m_BackgroundPoints */
- itk::SmartPointer<itk::FastMutexLock> m_PointSetsMutex;
+ std::mutex m_PointSetsMutex;
};
} // namespace mitk
#endif // MITKGRABCUTOPENCVIMAGEFILTER_H
diff --git a/Modules/OpenCVVideoSupport/Testing/mitkOpenCVToMitkImageFilterTest.cpp b/Modules/OpenCVVideoSupport/Testing/mitkOpenCVToMitkImageFilterTest.cpp
index 9ce7fa7591..7bab414242 100644
--- a/Modules/OpenCVVideoSupport/Testing/mitkOpenCVToMitkImageFilterTest.cpp
+++ b/Modules/OpenCVVideoSupport/Testing/mitkOpenCVToMitkImageFilterTest.cpp
@@ -1,231 +1,202 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// mitk includes
#include "mitkOpenCVToMitkImageFilter.h"
#include <mitkStandardFileLocations.h>
#include <mitkTestingMacros.h>
#include <mitkTestFixture.h>
-#include <itkMultiThreader.h>
#include <itksys/SystemTools.hxx>
#include <mitkIOUtil.h>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
+#include <thread>
/** Documentation
*
* @brief Objects of this class can start an internal thread by calling the Start() method.
* The thread is then updateing the tested object until the method Stop() is called. The class
* can be used to test if a filter is thread-save by using multiple objects and let
* them update simuntanously.
*/
class mitkTestOpenCVToMITKImageFilterThread : public itk::Object
{
public:
mitkClassMacroItkParent(mitkTestOpenCVToMITKImageFilterThread, itk::Object);
- mitkNewMacro1Param(mitkTestOpenCVToMITKImageFilterThread, itk::MultiThreader::Pointer);
+ itkNewMacro(mitkTestOpenCVToMITKImageFilterThread);
int NumberOfMessages;
protected:
- mitkTestOpenCVToMITKImageFilterThread(itk::MultiThreader::Pointer MultiThreader)
+ mitkTestOpenCVToMITKImageFilterThread()
{
- ThreadID = -1;
NumberOfMessages = 0;
- m_MultiThreader = MultiThreader;
-
}
bool ThreadRunning;
- int ThreadID;
+ std::thread Thread;
cv::Mat currentImage;
mitk::OpenCVToMitkImageFilter::Pointer m_testedFilter;
- itk::MultiThreader::Pointer m_MultiThreader;
-
void DoSomething()
{
while (ThreadRunning)
{
m_testedFilter->SetOpenCVMat(currentImage);
m_testedFilter->Update();
mitk::Image::Pointer result;
result = m_testedFilter->GetOutput();
- //std::cout << "Thread " << ThreadID << " Update Call" << std::endl;
}
}
- static ITK_THREAD_RETURN_TYPE ThreadStartTracking(void* pInfoStruct)
+ void ThreadStartTracking()
{
- /* extract this pointer from Thread Info structure */
- struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct;
- if (pInfo == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
- if (pInfo->UserData == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
- mitkTestOpenCVToMITKImageFilterThread *thisthread = (mitkTestOpenCVToMITKImageFilterThread*)pInfo->UserData;
-
- if (thisthread != nullptr)
- thisthread->DoSomething();
-
- return ITK_THREAD_RETURN_VALUE;
+ this->DoSomething();
}
public:
- int Start()
+ void Start()
{
ThreadRunning = true;
- this->ThreadID = m_MultiThreader->SpawnThread(this->ThreadStartTracking, this);
- return ThreadID;
+ this->Thread = std::thread(&mitkTestOpenCVToMITKImageFilterThread::ThreadStartTracking, this);
}
void Stop()
{
ThreadRunning = false;
+ if (this->Thread.joinable())
+ this->Thread.join();
}
void setFilter(mitk::OpenCVToMitkImageFilter::Pointer testedFilter)
{
m_testedFilter = testedFilter;
}
void setImage(cv::Mat image)
{
currentImage = image;
}
};
class mitkOpenCVToMitkImageFilterTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkOpenCVToMitkImageFilterTestSuite);
MITK_TEST(TestInitialization);
MITK_TEST(TestThreadSafety);
CPPUNIT_TEST_SUITE_END();
private:
cv::Mat image1,image2,image3,image4,image5;
mitk::OpenCVToMitkImageFilter::Pointer testFilter;
public:
void setUp() override
{
image1 = cv::imread(GetTestDataFilePath("NrrdWritingTestImage.jpg").c_str());
image2 = cv::imread(GetTestDataFilePath("Png2D-bw.png").c_str());
image3 = cv::imread(GetTestDataFilePath("OpenCV-Data/CroppedImage.png").c_str());
image4 = cv::imread(GetTestDataFilePath("OpenCV-Data/GrabCutMask.png").c_str());
image5 = cv::imread(GetTestDataFilePath("OpenCV-Data/GrabCutOutput.png").c_str());
testFilter = mitk::OpenCVToMitkImageFilter::New();
//change input
testFilter->SetOpenCVMat(image1);
}
void tearDown() override
{
}
void TestInitialization()
{
testFilter = mitk::OpenCVToMitkImageFilter::New();
MITK_TEST_OUTPUT(<<"Testing Initialization");
}
void TestThreadSafety()
{
- std::vector<unsigned int> threadIDs;
- std::vector<mitkTestOpenCVToMITKImageFilterThread::Pointer> threads;
- itk::MultiThreader::Pointer multiThreader = itk::MultiThreader::New();
MITK_TEST_OUTPUT(<< "Testing Thread Safety with 2 Threads");
//create two threads
- mitkTestOpenCVToMITKImageFilterThread::Pointer newThread1 = mitkTestOpenCVToMITKImageFilterThread::New(multiThreader);
+ auto newThread1 = mitkTestOpenCVToMITKImageFilterThread::New();
newThread1->setFilter(testFilter);
newThread1->setImage(image1);
- threads.push_back(newThread1);
- mitkTestOpenCVToMITKImageFilterThread::Pointer newThread2 = mitkTestOpenCVToMITKImageFilterThread::New(multiThreader);
+ auto newThread2 = mitkTestOpenCVToMITKImageFilterThread::New();
newThread2->setFilter(testFilter);
newThread2->setImage(image1);
- threads.push_back(newThread2);
-
//start both
- unsigned int id1 = newThread1->Start();
- unsigned int id2 = newThread2->Start();
+ newThread1->Start();
+ newThread2->Start();
int delay = 1;
- for (int i = 0; i < 10000; i++)
+ for (int i = 0; i < 100; i++)
{
//std::cout << "Run " << i << std::endl;
//wait a bit
itksys::SystemTools::Delay(delay);
//change input
newThread1->setImage(image2);
newThread1->setImage(image3);
//wait a bit
itksys::SystemTools::Delay(delay);
//change input
newThread1->setImage(image4);
newThread1->setImage(image5);
//wait a bit
itksys::SystemTools::Delay(delay);
//change input
newThread1->setImage(image2);
newThread1->setImage(image2);
//wait a bit
itksys::SystemTools::Delay(delay);
//change input
newThread1->setImage(image3);
newThread1->setImage(image3);
//wait a bit
itksys::SystemTools::Delay(delay);
}
//stop both threads
newThread1->Stop();
newThread2->Stop();
- multiThreader->TerminateThread(id1);
- multiThreader->TerminateThread(id2);
-
MITK_TEST_OUTPUT(<< "Testing Thread Safety with 2 Threads");
}
private:
};
MITK_TEST_SUITE_REGISTRATION(mitkOpenCVToMitkImageFilter)
diff --git a/Modules/OpenCVVideoSupport/mitkOpenCVToMitkImageFilter.cpp b/Modules/OpenCVVideoSupport/mitkOpenCVToMitkImageFilter.cpp
index fcffa8b612..ab427e98e9 100644
--- a/Modules/OpenCVVideoSupport/mitkOpenCVToMitkImageFilter.cpp
+++ b/Modules/OpenCVVideoSupport/mitkOpenCVToMitkImageFilter.cpp
@@ -1,155 +1,152 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkOpenCVToMitkImageFilter.h"
#include <itkImportImageFilter.h>
#include <itkRGBPixel.h>
#include <mitkITKImageImport.txx>
#include <itkOpenCVImageBridge.h>
#include <itkImageFileWriter.h>
#include "mitkImageToOpenCVImageFilter.h"
namespace mitk{
OpenCVToMitkImageFilter::OpenCVToMitkImageFilter()
{
- m_ImageMutex = itk::FastMutexLock::New();
- m_OpenCVMatMutex = itk::FastMutexLock::New();
}
OpenCVToMitkImageFilter::~OpenCVToMitkImageFilter()
{
}
void OpenCVToMitkImageFilter::SetOpenCVMat(const cv::Mat &image)
{
- m_OpenCVMatMutex->Lock();
+ m_OpenCVMatMutex.lock();
m_OpenCVMat = image;
- m_OpenCVMatMutex->Unlock();
+ m_OpenCVMatMutex.unlock();
this->Modified();
}
void OpenCVToMitkImageFilter::SetOpenCVImage(const IplImage* image)
{
const cv::Mat cvMat = cv::cvarrToMat(image, false);
this->SetOpenCVMat(cvMat);
}
void OpenCVToMitkImageFilter::GenerateData()
{
if (m_OpenCVMat.cols != 0 && m_OpenCVMat.rows != 0 && m_OpenCVMat.data)
{
// copy current cvMat
- m_OpenCVMatMutex->Lock();
+ m_OpenCVMatMutex.lock();
const cv::Mat input = m_OpenCVMat;
- m_OpenCVMatMutex->Unlock();
+ m_OpenCVMatMutex.unlock();
// convert cvMat to mitk::Image
- m_ImageMutex->Lock();
+ m_ImageMutex.lock();
// now convert rgb image
if ((input.depth() >= 0) && ((unsigned int)input.depth() == CV_8S) && (input.channels() == 1))
{
m_Image = ConvertCVMatToMitkImage< char, 2>(input);
}
else if (input.depth() == CV_8U && input.channels() == 1)
{
m_Image = ConvertCVMatToMitkImage< unsigned char, 2>(input);
}
else if (input.depth() == CV_8U && input.channels() == 3)
{
m_Image = ConvertCVMatToMitkImage< UCRGBPixelType, 2>(input);
}
else if (input.depth() == CV_16U && input.channels() == 1)
{
m_Image = ConvertCVMatToMitkImage< unsigned short, 2>(input);
}
else if (input.depth() == CV_16U && input.channels() == 3)
{
m_Image = ConvertCVMatToMitkImage< USRGBPixelType, 2>(input);
}
else if (input.depth() == CV_32F && input.channels() == 1)
{
m_Image = ConvertCVMatToMitkImage< float, 2>(input);
}
else if (input.depth() == CV_32F && input.channels() == 3)
{
m_Image = ConvertCVMatToMitkImage< FloatRGBPixelType, 2>(input);
}
else if (input.depth() == CV_64F && input.channels() == 1)
{
m_Image = ConvertCVMatToMitkImage< double, 2>(input);
}
else if (input.depth() == CV_64F && input.channels() == 3)
{
m_Image = ConvertCVMatToMitkImage< DoubleRGBPixelType, 2>(input);
}
else
{
MITK_WARN << "Unknown image depth and/or pixel type. Cannot convert OpenCV to MITK image.";
return;
}
- //inputMutex->Unlock();
- m_ImageMutex->Unlock();
+ m_ImageMutex.unlock();
}
else
{
MITK_WARN << "Cannot start filter. OpenCV Image not set.";
return;
}
}
ImageSource::OutputImageType* OpenCVToMitkImageFilter::GetOutput()
{
return m_Image;
}
/********************************************
* Converting from OpenCV image to ITK Image
*********************************************/
template <typename TPixel, unsigned int VImageDimension>
Image::Pointer mitk::OpenCVToMitkImageFilter::ConvertCVMatToMitkImage(const cv::Mat input)
{
typedef itk::Image< TPixel, VImageDimension > ImageType;
typename ImageType::Pointer output = itk::OpenCVImageBridge::CVMatToITKImage<ImageType>(input);
Image::Pointer mitkImage = Image::New();
mitkImage = GrabItkImageMemory(output);
return mitkImage;
}
void OpenCVToMitkImageFilter::InsertOpenCVImageAsMitkTimeSlice(cv::Mat openCVImage, Image::Pointer mitkImage, int timeStep)
{
// convert it to an mitk::Image
this->SetOpenCVMat(openCVImage);
this->Modified();
this->Update();
//insert it as a timeSlice
mitkImage->GetGeometry(timeStep)->SetSpacing(this->GetOutput()->GetGeometry()->GetSpacing());
mitkImage->GetGeometry(timeStep)->SetOrigin(this->GetOutput()->GetGeometry()->GetOrigin());
mitkImage->GetGeometry(timeStep)->SetIndexToWorldTransform(this->GetOutput()->GetGeometry()->GetIndexToWorldTransform());
mitk::ImageReadAccessor readAccess(this->GetOutput());
mitkImage->SetImportVolume(readAccess.GetData(), timeStep);
mitkImage->Modified();
mitkImage->Update();
- m_ImageMutex->Lock();
+ m_ImageMutex.lock();
m_Image = mitkImage;
- m_ImageMutex->Unlock();
+ m_ImageMutex.unlock();
}
} // end namespace mitk
diff --git a/Modules/OpenCVVideoSupport/mitkOpenCVToMitkImageFilter.h b/Modules/OpenCVVideoSupport/mitkOpenCVToMitkImageFilter.h
index 283f91bc90..c92612cadd 100644
--- a/Modules/OpenCVVideoSupport/mitkOpenCVToMitkImageFilter.h
+++ b/Modules/OpenCVVideoSupport/mitkOpenCVToMitkImageFilter.h
@@ -1,95 +1,96 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkOpenCVToMitkImageFilter_h
#define mitkOpenCVToMitkImageFilter_h
// mitk includes
#include <MitkOpenCVVideoSupportExports.h>
#include <mitkCommon.h>
#include <mitkImageSource.h>
// itk includes
#include <itkMacro.h>
-#include <itkFastMutexLock.h>
#include <itkImage.h>
// OpenCV includes
#include <opencv2/core.hpp>
+#include <mutex>
+
namespace mitk
{
///
/// \brief Filter for creating MITK RGB Images from an OpenCV image
///
class MITKOPENCVVIDEOSUPPORT_EXPORT OpenCVToMitkImageFilter : public ImageSource
{
public:
typedef itk::RGBPixel< unsigned char > UCRGBPixelType;
typedef itk::RGBPixel< unsigned short > USRGBPixelType;
typedef itk::RGBPixel< float > FloatRGBPixelType;
typedef itk::RGBPixel< double > DoubleRGBPixelType;
///
/// the static function for the conversion
///
template <typename TPixel, unsigned int VImageDimension>
static Image::Pointer ConvertCVMatToMitkImage(const cv::Mat input);
mitkClassMacro(OpenCVToMitkImageFilter, ImageSource);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
///
/// sets an iplimage as input
///
void SetOpenCVImage(const IplImage* image);
//itkGetMacro(OpenCVImage, const IplImage*);
///
/// sets an opencv mat as input (will be used if OpenCVImage Ipl image is 0)
///
void SetOpenCVMat(const cv::Mat& image);
itkGetMacro(OpenCVMat, cv::Mat);
OutputImageType* GetOutput(void);
//##Documentation
//## @brief Convenient method to insert an openCV image as a slice at a
//## certain time step into a 3D or 4D mitk::Image.
//##
//## @param openCVImage - the image that is inserted into the mitk Image
//## @param mitkImage - pointer to the mitkImage, which is changed by this method!
//## @param timeStep - the time step, at which the openCVImage is inserted
//##
//## @attention The parameter mitkImage will be changed!
void InsertOpenCVImageAsMitkTimeSlice(const cv::Mat openCVImage, Image::Pointer mitkImage, int timeStep);
protected:
OpenCVToMitkImageFilter(); // purposely hidden
~OpenCVToMitkImageFilter() override;
void GenerateData() override;
protected:
Image::Pointer m_Image;
cv::Mat m_OpenCVMat;
- itk::FastMutexLock::Pointer m_ImageMutex;
- itk::FastMutexLock::Pointer m_OpenCVMatMutex;
+ std::mutex m_ImageMutex;
+ std::mutex m_OpenCVMatMutex;
};
} // namespace mitk
#endif // mitkOpenCVToMitkImageFilter_h
diff --git a/Modules/OpenIGTLink/Filters/mitkImageToIGTLMessageFilter.cpp b/Modules/OpenIGTLink/Filters/mitkImageToIGTLMessageFilter.cpp
index 4ba7dcfabd..85ac128dc6 100644
--- a/Modules/OpenIGTLink/Filters/mitkImageToIGTLMessageFilter.cpp
+++ b/Modules/OpenIGTLink/Filters/mitkImageToIGTLMessageFilter.cpp
@@ -1,258 +1,262 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkImageToIGTLMessageFilter.h"
#include "mitkImageReadAccessor.h"
#include "itkByteSwapper.h"
#include "igtlImageMessage.h"
mitk::ImageToIGTLMessageFilter::ImageToIGTLMessageFilter()
{
mitk::IGTLMessage::Pointer output = mitk::IGTLMessage::New();
this->SetNumberOfRequiredOutputs(1);
this->SetNthOutput(0, output.GetPointer());
this->SetNumberOfRequiredInputs(1);
}
void mitk::ImageToIGTLMessageFilter::GenerateData()
{
// MITK_INFO << "ImageToIGTLMessageFilter.GenerateData()";
for (unsigned int i = 0; i < this->GetNumberOfIndexedOutputs(); ++i)
{
mitk::IGTLMessage* output = this->GetOutput(i);
assert(output);
const mitk::Image* img = this->GetInput(i);
int dims = img->GetDimension();
int chn = img->GetNumberOfChannels();
if (dims < 1)
{
MITK_ERROR << "Can not handle dimensionless images";
}
if (dims > 3)
{
MITK_ERROR << "Can not handle more than three dimensions";
continue;
}
if (chn != 1)
{
MITK_ERROR << "Can not handle anything but one channel. Image contained " << chn;
continue;
}
igtl::ImageMessage::Pointer imgMsg = igtl::ImageMessage::New();
// TODO: Which kind of coordinate system does MITK really use?
imgMsg->SetCoordinateSystem(igtl::ImageMessage::COORDINATE_RAS);
// We could do this based on the host endiannes, but that's weird.
// We instead use little endian, as most modern systems are little endian,
// so there will probably not be an endian swap involved.
imgMsg->SetEndian(igtl::ImageMessage::ENDIAN_LITTLE);
// Set number of components.
mitk::PixelType type = img->GetPixelType();
imgMsg->SetNumComponents(type.GetNumberOfComponents());
// Set scalar type.
switch (type.GetComponentType())
{
- case itk::ImageIOBase::CHAR:
+ case itk::IOComponentEnum::CHAR:
imgMsg->SetScalarTypeToInt8();
break;
- case itk::ImageIOBase::UCHAR:
+ case itk::IOComponentEnum::UCHAR:
imgMsg->SetScalarTypeToUint8();
break;
- case itk::ImageIOBase::SHORT:
+ case itk::IOComponentEnum::SHORT:
imgMsg->SetScalarTypeToInt16();
break;
- case itk::ImageIOBase::USHORT:
+ case itk::IOComponentEnum::USHORT:
imgMsg->SetScalarTypeToUint16();
break;
- case itk::ImageIOBase::INT:
+ case itk::IOComponentEnum::INT:
imgMsg->SetScalarTypeToInt32();
break;
- case itk::ImageIOBase::UINT:
+ case itk::IOComponentEnum::UINT:
imgMsg->SetScalarTypeToUint32();
break;
- case itk::ImageIOBase::LONG:
+ case itk::IOComponentEnum::LONG:
// OIGTL doesn't formally support 64bit int scalars, but if they are
// ever added,
// they will have the identifier 8 assigned.
imgMsg->SetScalarType(8);
break;
- case itk::ImageIOBase::ULONG:
+ case itk::IOComponentEnum::ULONG:
// OIGTL doesn't formally support 64bit uint scalars, but if they are
// ever added,
// they will have the identifier 9 assigned.
imgMsg->SetScalarType(9);
break;
- case itk::ImageIOBase::FLOAT:
+ case itk::IOComponentEnum::FLOAT:
// The igtl library has no method for this. Correct type is 10.
imgMsg->SetScalarType(10);
break;
- case itk::ImageIOBase::DOUBLE:
+ case itk::IOComponentEnum::DOUBLE:
// The igtl library has no method for this. Correct type is 11.
imgMsg->SetScalarType(11);
break;
default:
MITK_ERROR << "Can not handle pixel component type "
<< type.GetComponentType();
return;
}
// Set transformation matrix.
vtkMatrix4x4* matrix = img->GetGeometry()->GetVtkMatrix();
float matF[4][4];
for (size_t i = 0; i < 4; ++i)
{
for (size_t j = 0; j < 4; ++j)
{
matF[i][j] = matrix->GetElement(i, j);
}
}
imgMsg->SetMatrix(matF);
float spacing[3];
auto spacingImg = img->GetGeometry()->GetSpacing();
for (int i = 0; i < 3; ++i)
spacing[i] = spacingImg[i];
imgMsg->SetSpacing(spacing);
// Set dimensions.
int sizes[3];
for (size_t j = 0; j < 3; ++j)
{
sizes[j] = img->GetDimension(j);
}
imgMsg->SetDimensions(sizes);
// Allocate and copy data.
imgMsg->AllocatePack();
imgMsg->AllocateScalars();
size_t num_pixel = sizes[0] * sizes[1] * sizes[2];
void* out = imgMsg->GetScalarPointer();
{
// Scoped, so that readAccess will be released ASAP.
mitk::ImageReadAccessor readAccess(img, img->GetChannelData(0));
const void* in = readAccess.GetData();
memcpy(out, in, num_pixel * type.GetSize());
}
// We want to byte swap to little endian. We would like to just
// swap by number of bytes for each component, but itk::ByteSwapper
// is templated over element type, not over element size. So we need to
// switch on the size and use types of the same size.
size_t num_scalars = num_pixel * type.GetNumberOfComponents();
switch (type.GetComponentType())
{
- case itk::ImageIOBase::CHAR:
- case itk::ImageIOBase::UCHAR:
+ case itk::IOComponentEnum::CHAR:
+ case itk::IOComponentEnum::UCHAR:
// No endian conversion necessary, because a char is exactly one byte!
break;
- case itk::ImageIOBase::SHORT:
- case itk::ImageIOBase::USHORT:
+ case itk::IOComponentEnum::SHORT:
+ case itk::IOComponentEnum::USHORT:
itk::ByteSwapper<short>::SwapRangeFromSystemToLittleEndian((short*)out,
num_scalars);
break;
- case itk::ImageIOBase::INT:
- case itk::ImageIOBase::UINT:
+ case itk::IOComponentEnum::INT:
+ case itk::IOComponentEnum::UINT:
itk::ByteSwapper<int>::SwapRangeFromSystemToLittleEndian((int*)out,
num_scalars);
break;
- case itk::ImageIOBase::LONG:
- case itk::ImageIOBase::ULONG:
+ case itk::IOComponentEnum::LONG:
+ case itk::IOComponentEnum::ULONG:
itk::ByteSwapper<long>::SwapRangeFromSystemToLittleEndian((long*)out,
num_scalars);
break;
- case itk::ImageIOBase::FLOAT:
+ case itk::IOComponentEnum::FLOAT:
itk::ByteSwapper<float>::SwapRangeFromSystemToLittleEndian((float*)out,
num_scalars);
break;
- case itk::ImageIOBase::DOUBLE:
+ case itk::IOComponentEnum::DOUBLE:
itk::ByteSwapper<double>::SwapRangeFromSystemToLittleEndian(
(double*)out, num_scalars);
break;
+ default:
+ MITK_ERROR << "Can not handle pixel component type "
+ << type.GetComponentType();
+ return;
}
//copy timestamp of mitk image
igtl::TimeStamp::Pointer timestamp = igtl::TimeStamp::New();
timestamp->SetTime(img->GetMTime() / 1000, (int)(img->GetMTime()) % 1000);
imgMsg->SetTimeStamp(timestamp);
imgMsg->Pack();
output->SetMessage(imgMsg.GetPointer());
}
}
void mitk::ImageToIGTLMessageFilter::SetInput(const mitk::Image* img)
{
this->ProcessObject::SetNthInput(0, const_cast<mitk::Image*>(img));
this->CreateOutputsForAllInputs();
}
void mitk::ImageToIGTLMessageFilter::SetInput(unsigned int idx,
const Image* img)
{
this->ProcessObject::SetNthInput(idx, const_cast<mitk::Image*>(img));
this->CreateOutputsForAllInputs();
}
const mitk::Image* mitk::ImageToIGTLMessageFilter::GetInput(void)
{
if (this->GetNumberOfInputs() < 1)
return nullptr;
return static_cast<const mitk::Image*>(this->ProcessObject::GetInput(0));
}
const mitk::Image* mitk::ImageToIGTLMessageFilter::GetInput(unsigned int idx)
{
if (this->GetNumberOfInputs() < idx + 1)
{
return nullptr;
}
return static_cast<const mitk::Image*>(this->ProcessObject::GetInput(idx));
}
void mitk::ImageToIGTLMessageFilter::ConnectTo(mitk::ImageSource* upstream)
{
MITK_INFO << "Image source for this (" << this << ") mitkImageToIGTLMessageFilter is " << upstream;
for (DataObjectPointerArraySizeType i = 0; i < upstream->GetNumberOfOutputs();
i++)
{
this->SetInput(i, upstream->GetOutput(i));
}
}
void mitk::ImageToIGTLMessageFilter::CreateOutputsForAllInputs()
{
// create one message output for all image inputs
this->SetNumberOfIndexedOutputs(this->GetNumberOfIndexedInputs());
for (size_t idx = 0; idx < this->GetNumberOfIndexedOutputs(); ++idx)
{
if (this->GetOutput(idx) == nullptr)
{
this->SetNthOutput(idx, this->MakeOutput(idx));
}
this->Modified();
}
}
diff --git a/Modules/OpenIGTLink/mitkIGTLClient.cpp b/Modules/OpenIGTLink/mitkIGTLClient.cpp
index 63bc2659fc..649c6a808d 100644
--- a/Modules/OpenIGTLink/mitkIGTLClient.cpp
+++ b/Modules/OpenIGTLink/mitkIGTLClient.cpp
@@ -1,120 +1,117 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkIGTLClient.h"
//#include "mitkIGTTimeStamp.h"
//#include "mitkIGTHardwareException.h"
#include "igtlTrackingDataMessage.h"
#include <cstdio>
#include <itksys/SystemTools.hxx>
-#include <itkMutexLockHolder.h>
#include <igtlClientSocket.h>
#include <igtl_status.h>
-typedef itk::MutexLockHolder<itk::FastMutexLock> MutexLockHolder;
-
mitk::IGTLClient::IGTLClient(bool ReadFully) :
IGTLDevice(ReadFully)
{
}
mitk::IGTLClient::~IGTLClient()
{
}
bool mitk::IGTLClient::OpenConnection()
{
if (this->GetState() != Setup)
{
mitkThrowException(mitk::Exception) <<
"Can only try to open the connection if in setup mode. State was " << this->GetState();
return false;
}
std::string hostname = this->GetHostname();
int portNumber = this->GetPortNumber();
if (portNumber == -1 || hostname.size() <= 0)
{
//port number or hostname was not correct
MITK_WARN << "Port number or hostname was not correct";
return false;
}
//create a new client socket
m_Socket = igtl::ClientSocket::New();
//try to connect to the igtl server
int response = dynamic_cast<igtl::ClientSocket*>(m_Socket.GetPointer())->
ConnectToServer(hostname.c_str(), portNumber);
//check the response
if (response != 0)
{
MITK_ERROR << "The client could not connect to " << hostname << " port: " << portNumber;
return false;
}
// everything is initialized and connected so the communication can be started
this->SetState(Ready);
//inform observers about this new client
this->InvokeEvent(NewClientConnectionEvent());
return true;
}
void mitk::IGTLClient::Receive()
{
//MITK_INFO << "Trying to receive message";
//try to receive a message, if the socket is not present anymore stop the
//communication
unsigned int status = this->ReceivePrivate(this->m_Socket);
if (status == IGTL_STATUS_NOT_PRESENT)
{
this->StopCommunicationWithSocket(this->m_Socket);
//inform observers about loosing the connection to this socket
this->InvokeEvent(LostConnectionEvent());
MITK_WARN("IGTLClient") << "Lost connection to server socket.";
}
}
void mitk::IGTLClient::Send()
{
mitk::IGTLMessage::Pointer mitkMessage;
//get the latest message from the queue
mitkMessage = this->m_MessageQueue->PullSendMessage();
// there is no message => return
if (mitkMessage.IsNull())
return;
if (!this->SendMessagePrivate(mitkMessage, this->m_Socket))
{
MITK_WARN("IGTLDevice") << "Could not send the message.";
}
}
void mitk::IGTLClient::StopCommunicationWithSocket(igtl::Socket* /*socket*/)
{
- m_StopCommunicationMutex->Lock();
+ m_StopCommunicationMutex.lock();
m_StopCommunication = true;
- m_StopCommunicationMutex->Unlock();
+ m_StopCommunicationMutex.unlock();
}
unsigned int mitk::IGTLClient::GetNumberOfConnections()
{
return this->m_Socket->GetConnected();
}
diff --git a/Modules/OpenIGTLink/mitkIGTLDevice.cpp b/Modules/OpenIGTLink/mitkIGTLDevice.cpp
index 5cb079b7cd..8d531d085d 100644
--- a/Modules/OpenIGTLink/mitkIGTLDevice.cpp
+++ b/Modules/OpenIGTLink/mitkIGTLDevice.cpp
@@ -1,559 +1,496 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkIGTLDevice.h"
//#include "mitkIGTException.h"
//#include "mitkIGTTimeStamp.h"
-#include <itkMutexLockHolder.h>
+#include <itkMultiThreaderBase.h>
#include <itksys/SystemTools.hxx>
#include <cstring>
+#include <thread>
#include <igtlTransformMessage.h>
#include <mitkIGTLMessageCommon.h>
#include <igtl_status.h>
//remove later
#include <igtlTrackingDataMessage.h>
+namespace mitk
+{
+ itkEventMacroDefinition(MessageSentEvent, itk::AnyEvent);
+ itkEventMacroDefinition(MessageReceivedEvent, itk::AnyEvent);
+ itkEventMacroDefinition(CommandReceivedEvent, itk::AnyEvent);
+ itkEventMacroDefinition(NewClientConnectionEvent, itk::AnyEvent);
+ itkEventMacroDefinition(LostConnectionEvent, itk::AnyEvent);
+}
+
//TODO: Which timeout is acceptable and also needed to transmit image data? Is there a maximum data limit?
static const int SOCKET_SEND_RECEIVE_TIMEOUT_MSEC = 100;
-typedef itk::MutexLockHolder<itk::FastMutexLock> MutexLockHolder;
mitk::IGTLDevice::IGTLDevice(bool ReadFully) :
// m_Data(mitk::DeviceDataUnspecified),
m_State(mitk::IGTLDevice::Setup),
m_Name("Unspecified Device"),
m_StopCommunication(false),
m_Hostname("127.0.0.1"),
m_PortNumber(-1),
-m_LogMessages(false),
-m_MultiThreader(nullptr), m_SendThreadID(0), m_ReceiveThreadID(0), m_ConnectThreadID(0)
+m_LogMessages(false)
{
m_ReadFully = ReadFully;
- m_StopCommunicationMutex = itk::FastMutexLock::New();
- m_StateMutex = itk::FastMutexLock::New();
- // m_LatestMessageMutex = itk::FastMutexLock::New();
- m_SendingFinishedMutex = itk::FastMutexLock::New();
- m_ReceivingFinishedMutex = itk::FastMutexLock::New();
- m_ConnectingFinishedMutex = itk::FastMutexLock::New();
// execution rights are owned by the application thread at the beginning
- m_SendingFinishedMutex->Lock();
- m_ReceivingFinishedMutex->Lock();
- m_ConnectingFinishedMutex->Lock();
- m_MultiThreader = itk::MultiThreader::New();
+ m_SendingFinishedMutex.lock();
+ m_ReceivingFinishedMutex.lock();
+ m_ConnectingFinishedMutex.lock();
// m_Data = mitk::DeviceDataUnspecified;
// m_LatestMessage = igtl::MessageBase::New();
m_MessageFactory = mitk::IGTLMessageFactory::New();
m_MessageQueue = mitk::IGTLMessageQueue::New();
}
mitk::IGTLDevice::~IGTLDevice()
{
/* stop communication and disconnect from igtl device */
if (GetState() == Running)
{
this->StopCommunication();
this->CloseConnection();
}
else if (GetState() == Ready)
{
this->CloseConnection();
}
/* cleanup tracking thread */
- if (m_MultiThreader.IsNotNull())
- {
- if ((m_SendThreadID != 0))
- {
- m_MultiThreader->TerminateThread(m_SendThreadID);
- }
- if ((m_ReceiveThreadID != 0))
- {
- m_MultiThreader->TerminateThread(m_ReceiveThreadID);
- }
- if ((m_ConnectThreadID != 0))
- {
- m_MultiThreader->TerminateThread(m_ConnectThreadID);
- }
- }
- m_MultiThreader = nullptr;
+ if (m_SendThread.joinable())
+ m_SendThread.detach();
+
+ if (m_ReceiveThread.joinable())
+ m_ReceiveThread.detach();
+
+ if (m_ConnectThread.joinable())
+ m_ConnectThread.detach();
}
mitk::IGTLDevice::IGTLDeviceState mitk::IGTLDevice::GetState() const
{
- MutexLockHolder lock(*m_StateMutex);
+ std::lock_guard<std::mutex> lock(m_StateMutex);
return m_State;
}
void mitk::IGTLDevice::SetState(IGTLDeviceState state)
{
itkDebugMacro("setting m_State to " << state);
- m_StateMutex->Lock();
+ m_StateMutex.lock();
// MutexLockHolder lock(*m_StateMutex); // lock and unlock the mutex
if (m_State == state)
{
- m_StateMutex->Unlock();
+ m_StateMutex.unlock();
return;
}
m_State = state;
- m_StateMutex->Unlock();
+ m_StateMutex.unlock();
this->Modified();
}
bool mitk::IGTLDevice::TestConnection()
{
return true;
}
unsigned int mitk::IGTLDevice::ReceivePrivate(igtl::Socket* socket)
{
// Create a message buffer to receive header
igtl::MessageHeader::Pointer headerMsg;
headerMsg = igtl::MessageHeader::New();
// Initialize receive buffer
headerMsg->InitPack();
// Receive generic header from the socket
int r =
socket->Receive(headerMsg->GetPackPointer(), headerMsg->GetPackSize(), 0);
//MITK_INFO << "Server received r = " << r;
//MITK_INFO << "Received r = " << r;
if (r == 0) //connection error
{
// an error was received, therefore the communication with this socket
// must be stoppedy
return IGTL_STATUS_NOT_PRESENT;
}
else if (r == -1) //timeout
{
// a timeout was received, this is no error state, thus, do nothing
return IGTL_STATUS_TIME_OUT;
}
else if (r == headerMsg->GetPackSize())
{
// Deserialize the header and check the CRC
// ERROR HERE: This probably means the header data is corrupted...
int crcCheck = headerMsg->Unpack(1);
if (crcCheck & igtl::MessageHeader::UNPACK_HEADER)
{
// Allocate a time stamp
igtl::TimeStamp::Pointer ts;
ts = igtl::TimeStamp::New();
// Get time stamp
igtlUint32 sec;
igtlUint32 nanosec;
headerMsg->GetTimeStamp(ts);
ts->GetTimeStamp(&sec, &nanosec);
// std::cerr << "Time stamp: "
// << sec << "."
// << nanosec << std::endl;
// std::cerr << "Dev type and name: " << headerMsg->GetDeviceType() << " "
// << headerMsg->GetDeviceName() << std::endl;
// headerMsg->Print(std::cout);
//check the type of the received message
//if it is a GET_, STP_ or RTS_ command push it into the command queue
//otherwise continue reading the whole message from the socket
const char* curDevType = headerMsg->GetDeviceType();
if (std::strstr(curDevType, "GET_") != nullptr ||
std::strstr(curDevType, "STP_") != nullptr ||
std::strstr(curDevType, "RTS_") != nullptr)
{
this->m_MessageQueue->PushCommandMessage(headerMsg);
this->InvokeEvent(CommandReceivedEvent());
return IGTL_STATUS_OK;
}
//Create a message according to the header message
igtl::MessageBase::Pointer curMessage;
curMessage = m_MessageFactory->CreateInstance(headerMsg);
//check if the curMessage is created properly, if not the message type is
//not supported and the message has to be skipped
if (curMessage.IsNull())
{
socket->Skip(headerMsg->GetBodySizeToRead(), 0);
// MITK_ERROR("IGTLDevice") << "The received type is not supported. Please "
// "add it to the message factory.";
return IGTL_STATUS_NOT_FOUND;
}
//insert the header to the message and allocate the pack
curMessage->SetMessageHeader(headerMsg);
curMessage->AllocatePack();
// Receive transform data from the socket
int receiveCheck = 0;
receiveCheck = socket->Receive(curMessage->GetPackBodyPointer(),
curMessage->GetPackBodySize(), m_ReadFully);
if (receiveCheck > 0)
{
int c = curMessage->Unpack(1);
if (!(c & igtl::MessageHeader::UNPACK_BODY))
{
return IGTL_STATUS_CHECKSUM_ERROR;
}
//check the type of the received message
//if it is a command push it into the command queue
//otherwise into the normal receive queue
//STP_ commands are handled here because they implemented additional
//member variables that are not stored in the header message
if (std::strstr(curDevType, "STT_") != nullptr)
{
this->m_MessageQueue->PushCommandMessage(curMessage);
this->InvokeEvent(CommandReceivedEvent());
}
else
{
if(m_LogMessages)
MITK_INFO << "Received Message: " << mitk::IGTLMessage::New(curMessage)->ToString();
this->m_MessageQueue->PushMessage(curMessage);
this->InvokeEvent(MessageReceivedEvent());
}
return IGTL_STATUS_OK;
}
else
{
MITK_WARN("IGTLDevice") << "Received a valid header but could not "
<< "read the whole message.";
return IGTL_STATUS_UNKNOWN_ERROR;
}
}
else
{
//CRC check failed
MITK_WARN << "CRC Check failed";
return IGTL_STATUS_CHECKSUM_ERROR;
}
}
else
{
//Message size information and actual data size don't match.
//this state is not suppossed to be reached, return unknown error
MITK_WARN << "IGTL status unknown";
return IGTL_STATUS_UNKNOWN_ERROR;
}
}
void mitk::IGTLDevice::SendMessage(mitk::IGTLMessage::Pointer msg)
{
m_MessageQueue->PushSendMessage(msg);
}
unsigned int mitk::IGTLDevice::SendMessagePrivate(mitk::IGTLMessage::Pointer msg,
igtl::Socket::Pointer socket)
{
//check the input message
if (msg.IsNull())
{
MITK_ERROR("IGTLDevice") << "Could not send message because message is not "
"valid. Please check.";
return false;
}
igtl::MessageBase* sendMessage = msg->GetMessage();
// Pack (serialize) and send
sendMessage->Pack();
int sendSuccess = socket->Send(sendMessage->GetPackPointer(), sendMessage->GetPackSize());
if (sendSuccess)
{
if (m_LogMessages) { MITK_INFO << "Send IGTL message: " << msg->ToString(); }
this->InvokeEvent(MessageSentEvent());
return IGTL_STATUS_OK;
}
else
{
return IGTL_STATUS_UNKNOWN_ERROR;
}
}
-void mitk::IGTLDevice::RunCommunication(void (IGTLDevice::*ComFunction)(void), itk::FastMutexLock* mutex)
+void mitk::IGTLDevice::RunCommunication(void (IGTLDevice::*ComFunction)(void), std::mutex& mutex)
{
if (this->GetState() != Running)
return;
try
{
// keep lock until end of scope
- MutexLockHolder communicationFinishedLockHolder(*mutex);
+ std::lock_guard<std::mutex> communicationFinishedLockHolder(mutex);
// Because m_StopCommunication is used by two threads, access has to be guarded
// by a mutex. To minimize thread locking, a local copy is used here
bool localStopCommunication;
// update the local copy of m_StopCommunication
- this->m_StopCommunicationMutex->Lock();
+ this->m_StopCommunicationMutex.lock();
localStopCommunication = this->m_StopCommunication;
- this->m_StopCommunicationMutex->Unlock();
+ this->m_StopCommunicationMutex.unlock();
while ((this->GetState() == Running) && (localStopCommunication == false))
{
(this->*ComFunction)();
/* Update the local copy of m_StopCommunication */
- this->m_StopCommunicationMutex->Lock();
+ this->m_StopCommunicationMutex.lock();
localStopCommunication = m_StopCommunication;
- this->m_StopCommunicationMutex->Unlock();
+ this->m_StopCommunicationMutex.unlock();
// time to relax, this sets the maximum ever possible framerate to 1000 Hz
itksys::SystemTools::Delay(1);
}
}
catch (...)
{
- mutex->Unlock();
+ mutex.unlock();
this->StopCommunication();
MITK_ERROR("IGTLDevice::RunCommunication") << "Error while communicating. Thread stopped.";
//mitkThrowException(mitk::IGTException) << "Error while communicating. Thread stopped.";
}
// StopCommunication was called, thus the mode should be changed back to Ready now
// that the tracking loop has ended.
//this->SetState(Ready); //this is done elsewhere
MITK_DEBUG("IGTLDevice::RunCommunication") << "Reached end of communication.";
// returning from this function (and ThreadStartCommunication())
// this will end the thread
return;
}
bool mitk::IGTLDevice::StartCommunication()
{
if (this->GetState() != Ready)
return false;
// go to mode Running
this->SetState(Running);
// set a timeout for the sending and receiving
this->m_Socket->SetTimeout(SOCKET_SEND_RECEIVE_TIMEOUT_MSEC);
// update the local copy of m_StopCommunication
- this->m_StopCommunicationMutex->Lock();
+ this->m_StopCommunicationMutex.lock();
this->m_StopCommunication = false;
- this->m_StopCommunicationMutex->Unlock();
+ this->m_StopCommunicationMutex.unlock();
// transfer the execution rights to tracking thread
- m_SendingFinishedMutex->Unlock();
- m_ReceivingFinishedMutex->Unlock();
- m_ConnectingFinishedMutex->Unlock();
+ m_SendingFinishedMutex.unlock();
+ m_ReceivingFinishedMutex.unlock();
+ m_ConnectingFinishedMutex.unlock();
// start new threads that execute the communication
- m_SendThreadID =
- m_MultiThreader->SpawnThread(this->ThreadStartSending, this);
- m_ReceiveThreadID =
- m_MultiThreader->SpawnThread(this->ThreadStartReceiving, this);
- m_ConnectThreadID =
- m_MultiThreader->SpawnThread(this->ThreadStartConnecting, this);
+ m_SendThread = std::thread(&IGTLDevice::ThreadStartSending, this);
+ m_ReceiveThread = std::thread(&IGTLDevice::ThreadStartReceiving, this);
+ m_ConnectThread = std::thread(&IGTLDevice::ThreadStartConnecting, this);
// mitk::IGTTimeStamp::GetInstance()->Start(this);
return true;
}
bool mitk::IGTLDevice::StopCommunication()
{
if (this->GetState() == Running) // Only if the object is in the correct state
{
// m_StopCommunication is used by two threads, so we have to ensure correct
// thread handling
- m_StopCommunicationMutex->Lock();
+ m_StopCommunicationMutex.lock();
m_StopCommunication = true;
- m_StopCommunicationMutex->Unlock();
+ m_StopCommunicationMutex.unlock();
// we have to wait here that the other thread recognizes the STOP-command
// and executes it
- m_SendingFinishedMutex->Lock();
- m_ReceivingFinishedMutex->Lock();
- m_ConnectingFinishedMutex->Lock();
+ m_SendingFinishedMutex.lock();
+ m_ReceivingFinishedMutex.lock();
+ m_ConnectingFinishedMutex.lock();
// mitk::IGTTimeStamp::GetInstance()->Stop(this); // notify realtime clock
// StopCommunication was called, thus the mode should be changed back
// to Ready now that the tracking loop has ended.
this->SetState(Ready);
}
return true;
}
bool mitk::IGTLDevice::CloseConnection()
{
if (this->GetState() == Setup)
{
return true;
}
else if (this->GetState() == Running)
{
this->StopCommunication();
}
m_Socket->CloseSocket();
/* return to setup mode */
this->SetState(Setup);
// this->InvokeEvent(mitk::LostConnectionEvent());
return true;
}
bool mitk::IGTLDevice::SendRTSMessage(const char* type)
{
//construct the device type for the return message, it starts with RTS_ and
//continues with the requested type
std::string returnType("RTS_");
returnType.append(type);
//create a return message
igtl::MessageBase::Pointer rtsMsg =
this->m_MessageFactory->CreateInstance(returnType);
//if retMsg is nullptr there is no return message defined and thus it is not
//necessary to send one back
if (rtsMsg.IsNotNull())
{
this->SendMessage(mitk::IGTLMessage::New(rtsMsg));
return true;
}
else
{
return false;
}
}
void mitk::IGTLDevice::Connect()
{
MITK_DEBUG << "mitk::IGTLDevice::Connect();";
}
igtl::ImageMessage::Pointer mitk::IGTLDevice::GetNextImage2dMessage()
{
return this->m_MessageQueue->PullImage2dMessage();
}
igtl::ImageMessage::Pointer mitk::IGTLDevice::GetNextImage3dMessage()
{
return this->m_MessageQueue->PullImage3dMessage();
}
igtl::TransformMessage::Pointer mitk::IGTLDevice::GetNextTransformMessage()
{
return this->m_MessageQueue->PullTransformMessage();
}
igtl::TrackingDataMessage::Pointer mitk::IGTLDevice::GetNextTrackingDataMessage()
{
igtl::TrackingDataMessage::Pointer msg = this->m_MessageQueue->PullTrackingMessage();
return msg;
}
igtl::StringMessage::Pointer mitk::IGTLDevice::GetNextStringMessage()
{
return this->m_MessageQueue->PullStringMessage();
}
igtl::MessageBase::Pointer mitk::IGTLDevice::GetNextMiscMessage()
{
return this->m_MessageQueue->PullMiscMessage();
}
igtl::MessageBase::Pointer mitk::IGTLDevice::GetNextCommand()
{
return m_MessageQueue->PullCommandMessage();
}
void mitk::IGTLDevice::EnableNoBufferingMode(bool enable)
{
m_MessageQueue->EnableNoBufferingMode(enable);
}
void mitk::IGTLDevice::EnableNoBufferingMode(
mitk::IGTLMessageQueue::Pointer queue,
bool enable)
{
queue->EnableNoBufferingMode(enable);
}
-ITK_THREAD_RETURN_TYPE mitk::IGTLDevice::ThreadStartSending(void* pInfoStruct)
+void mitk::IGTLDevice::ThreadStartSending()
{
- /* extract this pointer from Thread Info structure */
- struct itk::MultiThreader::ThreadInfoStruct * pInfo =
- (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct;
- if (pInfo == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
- if (pInfo->UserData == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
- IGTLDevice *igtlDevice = (IGTLDevice*)pInfo->UserData;
- if (igtlDevice != nullptr)
- {
- igtlDevice->RunCommunication(&mitk::IGTLDevice::Send, igtlDevice->m_SendingFinishedMutex);
- }
- igtlDevice->m_SendThreadID = 0; // erase thread id because thread will end.
- return ITK_THREAD_RETURN_VALUE;
+ this->RunCommunication(&IGTLDevice::Send, m_SendingFinishedMutex);
}
-ITK_THREAD_RETURN_TYPE mitk::IGTLDevice::ThreadStartReceiving(void* pInfoStruct)
+void mitk::IGTLDevice::ThreadStartReceiving()
{
- /* extract this pointer from Thread Info structure */
- struct itk::MultiThreader::ThreadInfoStruct * pInfo =
- (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct;
- if (pInfo == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
- if (pInfo->UserData == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
- IGTLDevice *igtlDevice = (IGTLDevice*)pInfo->UserData;
- if (igtlDevice != nullptr)
- {
- igtlDevice->RunCommunication(&mitk::IGTLDevice::Receive,
- igtlDevice->m_ReceivingFinishedMutex);
- }
- igtlDevice->m_ReceiveThreadID = 0; // erase thread id because thread will end.
- return ITK_THREAD_RETURN_VALUE;
+ this->RunCommunication(&IGTLDevice::Receive, m_ReceivingFinishedMutex);
}
-ITK_THREAD_RETURN_TYPE mitk::IGTLDevice::ThreadStartConnecting(void* pInfoStruct)
+void mitk::IGTLDevice::ThreadStartConnecting()
{
- /* extract this pointer from Thread Info structure */
- struct itk::MultiThreader::ThreadInfoStruct * pInfo =
- (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct;
- if (pInfo == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
- if (pInfo->UserData == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
- IGTLDevice *igtlDevice = (IGTLDevice*)pInfo->UserData;
- if (igtlDevice != nullptr)
- {
- igtlDevice->RunCommunication(&mitk::IGTLDevice::Connect,
- igtlDevice->m_ConnectingFinishedMutex);
- }
- igtlDevice->m_ConnectThreadID = 0; // erase thread id because thread will end.
- return ITK_THREAD_RETURN_VALUE;
+ this->RunCommunication(&IGTLDevice::Connect, m_ConnectingFinishedMutex);
}
diff --git a/Modules/OpenIGTLink/mitkIGTLDevice.h b/Modules/OpenIGTLink/mitkIGTLDevice.h
index 57695c0f64..684d5953f2 100644
--- a/Modules/OpenIGTLink/mitkIGTLDevice.h
+++ b/Modules/OpenIGTLink/mitkIGTLDevice.h
@@ -1,413 +1,408 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKIGTLDEVICE_H
#define MITKIGTLDEVICE_H
+#include <mutex>
+#include <thread>
+
#include "mitkCommon.h"
//itk
#include "itkObject.h"
-#include "itkFastMutexLock.h"
-#include "itkMultiThreader.h"
//igtl
#include "igtlSocket.h"
#include "igtlMessageBase.h"
#include "igtlTransformMessage.h"
//mitkIGTL
#include "MitkOpenIGTLinkExports.h"
#include "mitkIGTLMessageFactory.h"
#include "mitkIGTLMessageQueue.h"
#include "mitkIGTLMessage.h"
namespace mitk {
/**
* \brief Interface for all OpenIGTLink Devices
*
* Defines the methods that are common for all devices using OpenIGTLink. It
* can open/close a connection, start/stop a communication and send/receive
* messages.
*
* It uses message queues to store the incoming and outgoing mails. They are
* configurable, you can set buffering on and off.
*
* The device is in one of three different states: Setup, Ready or Running.
* Setup is the initial state. From this state on you can call
* OpenConnection() and arrive in the Ready state. From the Ready state you
* call StartCommunication() to arrive in the Running state. Now the device
* is continuosly checking for new connections, receiving messages and
* sending messages. This runs in a seperate thread. To stop the communication
* call StopCommunication() (to arrive in Ready state) or CloseConnection()
* (to arrive in the Setup state).
*
* \ingroup OpenIGTLink
*
*/
class MITKOPENIGTLINK_EXPORT IGTLDevice : public itk::Object
{
public:
mitkClassMacroItkParent(IGTLDevice, itk::Object);
IGTLDevice(bool ReadFully);
/**
* \brief Type for state variable.
* The IGTLDevice is always in one of these states.
*
*/
enum IGTLDeviceState { Setup, Ready, Running };
/**
* \brief Opens a connection to the device
*
* This may only be called if there is currently no connection to the
* device. If OpenConnection() is successful, the object will change from
* Setup state to Ready state.
*/
virtual bool OpenConnection() = 0;
/**
* \brief Closes the connection to the device
*
* This may only be called if there is currently a connection to the
* device, but device is not running (e.g. object is in Ready state)
*/
virtual bool CloseConnection();
/**
* \brief Stops the communication between the two devices
*
* This may only be called if the device is in Running state.
*/
virtual bool StopCommunication();
/**
* \brief Starts the communication between the two devices
*
* This may only be called if the device is in Ready state.
*/
bool StartCommunication();
/**
* \brief Continuously calls the given function
*
* This may only be called if the device is in Running state and only from
* a seperate thread.
*
* \param ComFunction function pointer that specifies the method to be executed
* \param mutex the mutex that corresponds to the function pointer
*/
- void RunCommunication(void (IGTLDevice::*ComFunction)(void), itk::FastMutexLock* mutex);
+ void RunCommunication(void (IGTLDevice::*ComFunction)(void), std::mutex& mutex);
/**
* \brief Adds the given message to the sending queue
*
* This may only be called after the connection to the device has been
* established with a call to OpenConnection(). Note that the message
* is not send directly. This method just adds it to the send queue.
* \param msg The message to be added to the sending queue
*/
void SendMessage(mitk::IGTLMessage::Pointer msg);
/**
* \brief Returns current object state (Setup, Ready or Running)
*/
IGTLDeviceState GetState() const;
/**
* \brief Returns the oldest message in the command queue
* \return The oldest message from the command queue.
*/
igtl::MessageBase::Pointer GetNextCommand();
/**
* \brief Returns the oldest message in the receive queue
* \return The oldest message from the receive queue
*/
igtl::ImageMessage::Pointer GetNextImage2dMessage();
igtl::ImageMessage::Pointer GetNextImage3dMessage();
igtl::TransformMessage::Pointer GetNextTransformMessage();
igtl::TrackingDataMessage::Pointer GetNextTrackingDataMessage();
igtl::StringMessage::Pointer GetNextStringMessage();
igtl::MessageBase::Pointer GetNextMiscMessage();
/**
* \brief Sets the port number of the device
*/
itkSetMacro(PortNumber, int);
/**
* \brief Returns the port number of the device
*/
itkGetMacro(PortNumber, int);
/**
* \brief Sets the ip/hostname of the device
*/
itkSetMacro(Hostname, std::string);
/**
* \brief Returns the ip/hostname of the device
*/
itkGetMacro(Hostname, std::string);
/**
* \brief Returns the name of this device
*/
itkGetConstMacro(Name, std::string);
/**
* \brief Sets the name of this device
*/
itkSetMacro(Name, std::string);
/**
* \brief Advises this IGTL Device to always block until the whole message is read.
*/
itkSetMacro(ReadFully, bool);
/**
* \brief Returns a const reference to the receive queue
*/
itkGetConstMacro(MessageQueue, mitk::IGTLMessageQueue::Pointer);
/**
* \brief Returns the message factory
*/
itkGetMacro(MessageFactory, mitk::IGTLMessageFactory::Pointer);
/**
- * \brief static start method for the sending thread.
- * \param data a void pointer to the IGTLDevice object.
+ * \brief start method for the sending thread.
*/
- static ITK_THREAD_RETURN_TYPE ThreadStartSending(void* data);
+ void ThreadStartSending();
/**
- * \brief static start method for the receiving thread.
- * \param data a void pointer to the IGTLDevice object.
+ * \brief start method for the receiving thread.
*/
- static ITK_THREAD_RETURN_TYPE ThreadStartReceiving(void* data);
+ void ThreadStartReceiving();
/**
- * \brief static start method for the connection thread.
- * \param data a void pointer to the IGTLDevice object.
+ * \brief start method for the connection thread.
*/
- static ITK_THREAD_RETURN_TYPE ThreadStartConnecting(void* data);
+ void ThreadStartConnecting();
/**
* \brief TestConnection() tries to connect to a IGTL device on the current
* ip and port
*
* \todo Implement this method. Send a status message and check the answer.
*
* TestConnection() tries to connect to a IGTL server on the current
* ip and port and returns which device it has found.
* \return It returns the type of the device that answers. Throws an
* exception
* if no device is available on that ip/port.
* @throw mitk::Exception Throws an exception if there are errors
* while connecting to the device.
*/
virtual bool TestConnection();
/**
* \brief Send RTS message of given type
*/
bool SendRTSMessage(const char* type);
/**
* \brief Sets the buffering mode of the given queue
*/
void EnableNoBufferingMode(mitk::IGTLMessageQueue::Pointer queue,
bool enable = true);
void EnableNoBufferingMode(bool enable = true);
/**
* \brief Returns the number of connections of this device
*/
virtual unsigned int GetNumberOfConnections() = 0;
itkGetMacro(LogMessages, bool);
itkSetMacro(LogMessages, bool);
protected:
/**
* \brief Sends a message.
*
* This may only be called after the connection to the device has been
* established with a call to OpenConnection(). This method uses the given
* socket to send the given MessageReceivedEvent
*
* \param msg the message to be sent
* \param socket the socket used to communicate with the other device
*
* \retval IGTL_STATUS_OK the message was sent
* \retval IGTL_STATUS_UNKONWN_ERROR the message was not sent because an
* unknown error occurred
*/
unsigned int SendMessagePrivate(mitk::IGTLMessage::Pointer msg,
igtl::Socket::Pointer socket);
/**
* \brief Call this method to receive a message.
*
* The message will be saved in the receive queue.
*/
virtual void Receive() = 0;
/**
* \brief Call this method to receive a message from the given device.
*
* The message will be saved in the receive queue.
*
* \param device the socket that connects this device with the other one.
*
* \retval IGTL_STATUS_OK a message or a command was received
* \retval IGTL_STATUS_NOT_PRESENT the socket is not connected anymore
* \retval IGTL_STATUS_TIME_OUT the socket timed out
* \retval IGTL_STATUS_CHECKSUM_ERROR the checksum of the received msg was
* incorrect
* \retval IGTL_STATUS_UNKNOWN_ERROR an unknown error occurred
*/
unsigned int ReceivePrivate(igtl::Socket* device);
/**
* \brief Call this method to send a message. The message will be read from
* the queue.
*/
virtual void Send() = 0;
/**
* \brief Call this method to check for other devices that want to connect
* to this one.
*
* In case of a client this method is doing nothing. In case of a server it
* is checking for other devices and if there is one it establishes a
* connection.
*/
virtual void Connect();
/**
* \brief Stops the communication with the given socket
*
*/
virtual void StopCommunicationWithSocket(igtl::Socket* socket) = 0;
/**
* \brief change object state
*/
void SetState(IGTLDeviceState state);
IGTLDevice();
~IGTLDevice() override;
/** current object state (Setup, Ready or Running) */
IGTLDeviceState m_State;
/** the name of this device */
std::string m_Name;
/** signal used to stop the thread*/
bool m_StopCommunication;
/** mutex to control access to m_StopCommunication */
- itk::FastMutexLock::Pointer m_StopCommunicationMutex;
+ std::mutex m_StopCommunicationMutex;
/** mutex used to make sure that the send thread is just started once */
- itk::FastMutexLock::Pointer m_SendingFinishedMutex;
+ std::mutex m_SendingFinishedMutex;
/** mutex used to make sure that the receive thread is just started once */
- itk::FastMutexLock::Pointer m_ReceivingFinishedMutex;
+ std::mutex m_ReceivingFinishedMutex;
/** mutex used to make sure that the connect thread is just started once */
- itk::FastMutexLock::Pointer m_ConnectingFinishedMutex;
+ std::mutex m_ConnectingFinishedMutex;
/** mutex to control access to m_State */
- itk::FastMutexLock::Pointer m_StateMutex;
+ mutable std::mutex m_StateMutex;
/** the hostname or ip of the device */
std::string m_Hostname;
/** the port number of the device */
int m_PortNumber;
/** the socket used to communicate with other IGTL devices */
igtl::Socket::Pointer m_Socket;
/** The message receive queue */
mitk::IGTLMessageQueue::Pointer m_MessageQueue;
/** A message factory that provides the New() method for all msg types */
mitk::IGTLMessageFactory::Pointer m_MessageFactory;
bool m_LogMessages;
private:
- /** creates worker thread that continuously polls interface for new
- messages */
- itk::MultiThreader::Pointer m_MultiThreader;
- /** ID of sending thread */
- int m_SendThreadID;
- /** ID of receiving thread */
- int m_ReceiveThreadID;
- /** ID of connecting thread */
- int m_ConnectThreadID;
+ /** Sending thread */
+ std::thread m_SendThread;
+ /** Receiving thread */
+ std::thread m_ReceiveThread;
+ /** Connecting thread */
+ std::thread m_ConnectThread;
/** Always try to read the full message. */
bool m_ReadFully;
};
/**
* \brief connect to this Event to get notified when a message was successfully sent
*
* \note This event is invoked in the communication thread, therefore do not use it to make
* changes in the GUI!!! Use the QT signal slot system to decouple this call from the com thread
* */
- itkEventMacro(MessageSentEvent, itk::AnyEvent);
+ itkEventMacroDeclaration(MessageSentEvent, itk::AnyEvent);
/**
* \brief connect to this Event to get notified when a message was received
*
* \note Check if you can invoke this events like this or if you have to make
* it thread-safe. They are not invoked in the main thread!!!
* */
- itkEventMacro(MessageReceivedEvent, itk::AnyEvent);
+ itkEventMacroDeclaration(MessageReceivedEvent, itk::AnyEvent);
/**
* \brief connect to this Event to get notified when a command was received
*
* \note Check if you can invoke this events like this or if you have to make
* it thread-safe. They are not invoked in the main thread!!!
* */
- itkEventMacro(CommandReceivedEvent, itk::AnyEvent);
+ itkEventMacroDeclaration(CommandReceivedEvent, itk::AnyEvent);
/**
* \brief connect to this Event to get notified when another igtl device
* connects with this device.
*
* \note Check if you can invoke this events like this or if you have to make
* it thread-safe. They are not invoked in the main thread!!!
* */
- itkEventMacro(NewClientConnectionEvent, itk::AnyEvent);
+ itkEventMacroDeclaration(NewClientConnectionEvent, itk::AnyEvent);
/**
* \brief connect to this Event to get notified when this device looses the
* connection to a socket.
*
* \note Check if you can invoke this events like this or if you have to make
* it thread-safe. They are not invoked in the main thread!!!
* */
- itkEventMacro(LostConnectionEvent, itk::AnyEvent);
+ itkEventMacroDeclaration(LostConnectionEvent, itk::AnyEvent);
} // namespace mitk
#endif /* MITKIGTLDEVICE_H */
diff --git a/Modules/OpenIGTLink/mitkIGTLMessageProvider.cpp b/Modules/OpenIGTLink/mitkIGTLMessageProvider.cpp
index 5ebfdad595..5217d8b932 100644
--- a/Modules/OpenIGTLink/mitkIGTLMessageProvider.cpp
+++ b/Modules/OpenIGTLink/mitkIGTLMessageProvider.cpp
@@ -1,444 +1,383 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkIGTLMessageProvider.h"
#include "mitkIGTLDevice.h"
#include "mitkIGTLMessage.h"
#include "mitkIGTLMessageFactory.h"
#include "mitkCallbackFromGUIThread.h"
//Microservices
#include "usServiceReference.h"
#include "usModuleContext.h"
#include "usServiceEvent.h"
#include "mitkServiceInterface.h"
#include "usGetModuleContext.h"
//igt (remove this later)
#include "igtlBindMessage.h"
#include "igtlQuaternionTrackingDataMessage.h"
#include "igtlTrackingDataMessage.h"
#ifndef WIN32
#include <unistd.h>
#endif
+namespace mitk
+{
+ itkEventMacroDefinition(StreamingStartRequiredEvent, itk::AnyEvent);
+ itkEventMacroDefinition(StreamingStopRequiredEvent, itk::AnyEvent);
+}
+
mitk::IGTLMessageProvider::IGTLMessageProvider()
: mitk::IGTLDeviceSource()
{
this->SetName("IGTLMessageProvider");
- //m_MultiThreader = itk::MultiThreader::New();
- m_StreamingTimeMutex = itk::FastMutexLock::New();
- //m_StopStreamingThreadMutex = itk::FastMutexLock::New();
- //m_ThreadId = 0;
m_IsStreaming = false;
// Create a command object. The function will be called later from the main thread
this->m_StopStreamingCommand = ProviderCommand::New();
m_StopStreamingCommand->SetCallbackFunction(this,
&mitk::IGTLMessageProvider::InvokeStopStreamingEvent);
this->m_StreamingCommand = ProviderCommand::New();
m_StreamingCommand->SetCallbackFunction(this,
&mitk::IGTLMessageProvider::InvokeStartStreamingEvent);
}
mitk::IGTLMessageProvider::~IGTLMessageProvider()
{
- //// terminate worker thread on destruction
- //this->m_StopStreamingThreadMutex->Lock();
- //this->m_StopStreamingThread = true;
- //this->m_StopStreamingThreadMutex->Unlock();
- //if ( m_ThreadId >= 0)
- //{
- // this->m_MultiThreader->TerminateThread(m_ThreadId);
- //}
- this->InvokeEvent(StreamingStartRequiredEvent());
+ this->InvokeEvent(StreamingStartRequiredEvent());
}
void mitk::IGTLMessageProvider::Update()
{
Superclass::Update();
if (this->GetInput() != nullptr)
{
igtl::MessageBase::Pointer curMessage = this->GetInput()->GetMessage();
if (dynamic_cast<igtl::TrackingDataMessage*>(curMessage.GetPointer()) != nullptr)
{
igtl::TrackingDataMessage* tdMsg =
(igtl::TrackingDataMessage*)(curMessage.GetPointer());
igtl::TrackingDataElement::Pointer trackingData = igtl::TrackingDataElement::New();
tdMsg->GetTrackingDataElement(0, trackingData);
float x_pos, y_pos, z_pos;
trackingData->GetPosition(&x_pos, &y_pos, &z_pos);
}
}
}
void mitk::IGTLMessageProvider::GenerateData()
{
if (this->m_IGTLDevice.IsNull())
return;
for (unsigned int index = 0; index < this->GetNumberOfIndexedInputs(); index++)
{
mitk::IGTLMessage::Pointer msg = const_cast<mitk::IGTLMessage*>(this->GetInput(index));
if (msg == nullptr)
{
continue;
}
if ( !msg->IsDataValid() )
{
continue;
}
this->m_IGTLDevice->SendMessage(msg);
}
}
void mitk::IGTLMessageProvider::CreateOutputs()
{
//if outputs are set then delete them
if (this->GetNumberOfOutputs() > 0)
{
for (int numOP = this->GetNumberOfOutputs() - 1; numOP >= 0; numOP--)
this->RemoveOutput(numOP);
this->Modified();
}
//fill the outputs if a valid OpenIGTLink device is set
if (m_IGTLDevice.IsNull())
return;
this->SetNumberOfIndexedOutputs(1);
if (this->GetOutput(0) == nullptr)
{
DataObjectPointer newOutput = this->MakeOutput(0);
this->SetNthOutput(0, newOutput);
this->Modified();
}
}
//void mitk::IGTLMessageProvider::UpdateOutputInformation()
//{
// this->Modified(); // make sure that we need to be updated
// Superclass::UpdateOutputInformation();
//}
void mitk::IGTLMessageProvider::OnIncomingMessage()
{
}
void mitk::IGTLMessageProvider::OnLostConnection()
{
//in case the provider is streaming at the moment we have to stop it
if (m_IsStreaming)
{
MITK_DEBUG("IGTLMessageProvider") << "lost connection, stop streaming";
this->StopStreamingOfAllSources();
}
}
std::string RemoveRequestPrefixes(std::string requestType)
{
return requestType.substr(4);
}
void mitk::IGTLMessageProvider::OnIncomingCommand()
{
//get the next command
igtl::MessageBase::Pointer curCommand = this->m_IGTLDevice->GetNextCommand();
//extract the type
const char * requestType = curCommand->GetDeviceType();
//check the type
std::string reqType(requestType);
bool isGetMsg = !reqType.find("GET_");
bool isSTTMsg = !reqType.find("STT_");
bool isSTPMsg = !reqType.find("STP_");
//get the type from the request type (remove STT_, STP_, GET_, RTS_)
std::string type = RemoveRequestPrefixes(requestType);
//check all microservices if there is a fitting source for the requested type
mitk::IGTLMessageSource::Pointer source = this->GetFittingSource(type.c_str());
//if there is no fitting source return a RTS message, if there is a RTS
//type defined in the message factory send it
if ( source.IsNull() )
{
if ( !this->GetIGTLDevice()->SendRTSMessage(type.c_str()) )
{
//sending RTS message failed, probably because the type is not in the
//message factory
MITK_WARN("IGTLMessageProvider") << "Tried to send a RTS message but did "
"not succeed. Check if this type ( "
<< type << " ) was added to the message "
"factory. ";
}
}
else
{
if ( isGetMsg ) //if it is a single value push it into sending queue
{
//first it is necessary to update the source. This needs additional time
//but is necessary. But are we really allowed to call this here? In which
//thread are we? Is the source thread safe?
source->Update();
mitk::IGTLMessage::Pointer sourceOutput = source->GetOutput();
if (sourceOutput.IsNotNull() && sourceOutput->IsDataValid())
{
if ( source.IsNotNull() )
{
this->GetIGTLDevice()->SendMessage(sourceOutput);
}
}
}
else if ( isSTTMsg )
{
//read the requested frames per second
int fps = 10;
//read the fps from the command
igtl::MessageBase* curCommandPt = curCommand.GetPointer();
if ( std::strcmp( curCommand->GetDeviceType(), "STT_BIND" ) == 0 )
{
fps = ((igtl::StartBindMessage*)curCommandPt)->GetResolution();
}
else if ( std::strcmp( curCommand->GetDeviceType(), "STT_QTDATA" ) == 0 )
{
fps = ((igtl::StartQuaternionTrackingDataMessage*)curCommandPt)->GetResolution();
}
else if ( std::strcmp( curCommand->GetDeviceType(), "STT_TDATA" ) == 0 )
{
fps = ((igtl::StartTrackingDataMessage*)curCommandPt)->GetResolution();
}
this->StartStreamingOfSource(source, fps);
}
else if ( isSTPMsg )
{
this->StopStreamingOfSource(source);
}
else
{
//do nothing
}
}
}
bool mitk::IGTLMessageProvider::IsStreaming()
{
return m_IsStreaming;
}
void mitk::IGTLMessageProvider::StartStreamingOfSource(IGTLMessageSource* src,
unsigned int fps)
{
if ( src == nullptr )
return;
//so far the provider allows the streaming of a single source only
//if the streaming thread is already running return a RTS message
if ( !m_IsStreaming )
{
//if it is a stream establish a connection between the provider and the
//source
this->ConnectTo(src);
// calculate the streaming time
- this->m_StreamingTimeMutex->Lock();
+ this->m_StreamingTimeMutex.lock();
this->m_StreamingTime = 1.0 / (double) fps * 1000.0;
- this->m_StreamingTimeMutex->Unlock();
+ this->m_StreamingTimeMutex.unlock();
//// For streaming we need a continues time signal, since there is no timer
//// available we start a thread that generates a timing signal
//// This signal is invoked from the other thread the update of the pipeline
//// has to be executed from the main thread. Thus, we use the
//// callbackfromGUIThread class to pass the execution to the main thread
//this->m_ThreadId = m_MultiThreader->SpawnThread(this->TimerThread, this);
mitk::CallbackFromGUIThread::GetInstance()->CallThisFromGUIThread(
this->m_StreamingCommand);
this->m_IsStreaming = true;
}
else
{
MITK_WARN("IGTLMessageProvider") << "This provider just supports the "
"streaming of one source.";
}
}
void mitk::IGTLMessageProvider::InvokeStartStreamingEvent()
{
this->InvokeEvent(StreamingStartRequiredEvent());
}
void mitk::IGTLMessageProvider::InvokeStopStreamingEvent()
{
this->InvokeEvent(StreamingStopRequiredEvent());
}
void mitk::IGTLMessageProvider::StopStreamingOfSource(IGTLMessageSource* src)
{
//this is something bad!!! The streaming thread has to be stopped before the
//source is disconnected otherwise it can cause a crash. This has to be added!!
this->DisconnectFrom(src);
- //this->m_StopStreamingThreadMutex->Lock();
- //this->m_StopStreamingThread = true;
- //this->m_StopStreamingThreadMutex->Unlock();
-
mitk::CallbackFromGUIThread::GetInstance()->CallThisFromGUIThread(
this->m_StopStreamingCommand);
//does this flag needs a mutex???
this->m_IsStreaming = false;
}
void mitk::IGTLMessageProvider::StopStreamingOfAllSources()
{
// \todo remove all inputs
mitk::CallbackFromGUIThread::GetInstance()->CallThisFromGUIThread(
this->m_StopStreamingCommand);
//does this flag needs a mutex???
this->m_IsStreaming = false;
}
mitk::IGTLMessageSource::Pointer mitk::IGTLMessageProvider::GetFittingSource(const char* requestedType)
{
//get the context
us::ModuleContext* context = us::GetModuleContext();
//define the interface name
std::string interface = mitk::IGTLMessageSource::US_INTERFACE_NAME;
//specify a filter that defines the requested type
std::string filter = "(" + mitk::IGTLMessageSource::US_PROPKEY_DEVICETYPE +
"=" + requestedType + ")";
//find the fitting service
std::vector<us::ServiceReferenceU> serviceReferences =
context->GetServiceReferences(interface, filter);
//check if a service reference was found. It is also possible that several
//services were found. This is not checked here, just the first one is taken.
if ( serviceReferences.size() )
{
mitk::IGTLMessageSource::Pointer curSource =
context->GetService<mitk::IGTLMessageSource>(serviceReferences.front());
if ( curSource.IsNotNull() )
return curSource;
}
//no service reference was found or found service reference has no valid source
return nullptr;
}
void mitk::IGTLMessageProvider::Send(mitk::IGTLMessage::Pointer msg)
{
if (msg != nullptr)
{
MITK_INFO << "Sending OpenIGTLink Message: " << msg->ToString();
this->m_IGTLDevice->SendMessage(msg);
}
}
void
mitk::IGTLMessageProvider::ConnectTo( mitk::IGTLMessageSource* UpstreamFilter )
{
for (DataObjectPointerArraySizeType i = 0;
i < UpstreamFilter->GetNumberOfOutputs(); i++)
{
this->SetInput(i, UpstreamFilter->GetOutput(i));
}
}
void
mitk::IGTLMessageProvider::DisconnectFrom( mitk::IGTLMessageSource* UpstreamFilter )
{
if (UpstreamFilter == nullptr)
return;
for (DataObjectPointerArraySizeType i = 0; i < UpstreamFilter->GetNumberOfOutputs(); ++i)
{
auto input = UpstreamFilter->GetOutput(i);
if (input == nullptr)
continue;
auto nb = this->GetNumberOfIndexedInputs();
for (DataObjectPointerArraySizeType i = 0; i < nb; ++i)
{
if (this->GetInput(i) == input)
{
this->RemoveInput(i);
break;
}
}
}
}
-
-//ITK_THREAD_RETURN_TYPE mitk::IGTLMessageProvider::TimerThread(void* pInfoStruct)
-//{
-// // extract this pointer from thread info structure
-// struct itk::MultiThreader::ThreadInfoStruct * pInfo =
-// (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct;
-// mitk::IGTLMessageProvider* thisObject =
-// static_cast<mitk::IGTLMessageProvider*>(pInfo->UserData);
-//
-// itk::SimpleMutexLock mutex;
-// mutex.Lock();
-//
-// thisObject->m_StopStreamingThreadMutex->Lock();
-// thisObject->m_StopStreamingThread = false;
-// thisObject->m_StopStreamingThreadMutex->Unlock();
-//
-// thisObject->m_StreamingTimeMutex->Lock();
-// unsigned int waitingTime = thisObject->m_StreamingTime;
-// thisObject->m_StreamingTimeMutex->Unlock();
-//
-// while (true)
-// {
-// thisObject->m_StopStreamingThreadMutex->Lock();
-// bool stopThread = thisObject->m_StopStreamingThread;
-// thisObject->m_StopStreamingThreadMutex->Unlock();
-//
-// if (stopThread)
-// {
-// break;
-// }
-//
-// //wait for the time given
-// //I know it is not the nicest solution but we just need an approximate time
-// //sleeps for 20 ms
-// #if defined (WIN32) || defined (_WIN32)
-// Sleep(waitingTime);
-// #else
-// usleep(waitingTime * 1000);
-// #endif
-//
-// // Ask to execute that command from the GUI thread
-// mitk::CallbackFromGUIThread::GetInstance()->CallThisFromGUIThread(
-// thisObject->m_StreamingCommand);
-// }
-//
-// thisObject->m_ThreadId = 0;
-//
-// mutex.Unlock();
-//
-// return ITK_THREAD_RETURN_VALUE;
-//}
diff --git a/Modules/OpenIGTLink/mitkIGTLMessageProvider.h b/Modules/OpenIGTLink/mitkIGTLMessageProvider.h
index 1b158d0f56..17e0a9355a 100644
--- a/Modules/OpenIGTLink/mitkIGTLMessageProvider.h
+++ b/Modules/OpenIGTLink/mitkIGTLMessageProvider.h
@@ -1,221 +1,221 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef IGTLMESSAGEPROVIDER_H_HEADER_INCLUDED_
#define IGTLMESSAGEPROVIDER_H_HEADER_INCLUDED_
#include "mitkIGTLDevice.h"
#include "mitkIGTLDeviceSource.h"
//itk
#include "itkCommand.h"
namespace mitk {
/**
* \brief Provides information/objects from a MITK-Pipeline to other OpenIGTLink
* devices
*
* This class is intended as the drain of the pipeline. Other OpenIGTLink
* devices connect with the IGTLDevice hold by this provider. The other device
* asks for a certain data type. The provider checks if there are other
* IGTLMessageSources available that provide this data type. If yes the provider
* connects with this source and sends the message to the requesting device.
*
* If a STT message was received the provider looks for fitting messageSources.
* Once found it connects with it, starts a timing thread (which updates the
* pipeline) and sends the result to the requesting device.
*
* If a GET message was received the provider just calls an update of the
* found source and sends the result without connecting to the source.
*
* If a STP message was received it stops the thread and disconnects from the
* previous source.
*
* So far the provider can just connect with one source.
*
* \ingroup OpenIGTLink
*/
class MITKOPENIGTLINK_EXPORT IGTLMessageProvider : public IGTLDeviceSource
{
public:
mitkClassMacro(IGTLMessageProvider, IGTLDeviceSource);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
typedef itk::SimpleMemberCommand<mitk::IGTLMessageProvider> ProviderCommand;
/**
* \brief sends the msg to the requesting client
*
* Note: so far it broadcasts the message to all registered clients
*/
void Send(mitk::IGTLMessage::Pointer msg);
/**
* \brief Starts the streaming of the given message source with the given fps.
*/
void StartStreamingOfSource(mitk::IGTLMessageSource* src,
unsigned int fps);
/**
* \brief Stops the streaming of the given message source.
*/
void StopStreamingOfSource(mitk::IGTLMessageSource* src);
/**
* \brief Stops the streaming of all message source.
*/
void StopStreamingOfAllSources();
/**
* \brief Returns the streaming state.
*/
bool IsStreaming();
/**
* \brief Get method for the streaming time
*/
itkGetMacro(StreamingTime, unsigned int);
void Update() override;
protected:
IGTLMessageProvider();
~IGTLMessageProvider() override;
/**
* \brief filter execute method
*
* queries the OpenIGTLink device for new messages and updates its output
* igtl::MessageBase objects with it.
* \warning Will raise a std::out_of_range exception, if tools were added to
* the OpenIGTLink device after it was set as input for this filter
*/
void GenerateData() override;
/**
* \brief Create the necessary outputs for the m_IGTLDevice
*
* This Method is called internally whenever outputs need to be reset. Old
* Outputs are deleted when called.
**/
void CreateOutputs();
/**
* \brief This method is called when the IGTL device hold by this class
* receives a new message
**/
void OnIncomingMessage() override;
/**
* \brief This method is called when the IGTL device hold by this class
* receives a new command
**/
void OnIncomingCommand() override;
/**
* \brief This method is called when the IGTL device lost the connection to the other side
**/
void OnLostConnection() override;
/**
*\brief Connects the input of this filter to the outputs of the given
* IGTLMessageSource
*
* This method does not support smartpointer. use FilterX.GetPointer() to
* retrieve a dumbpointer.
*/
void ConnectTo( mitk::IGTLMessageSource* UpstreamFilter );
/**
*\brief Disconnects this filter from the outputs of the given
* IGTLMessageSource
*
* This method does not support smartpointer. use FilterX.GetPointer() to
* retrieve a dumbpointer.
*/
void DisconnectFrom( mitk::IGTLMessageSource* UpstreamFilter );
/**
* \brief Looks for microservices that provide messages with the requested
* type.
**/
mitk::IGTLMessageSource::Pointer GetFittingSource(const char* requestedType);
/** Invokes the start streaming event. This separate method is required, because it
* has to be started from the main thread. (It is used as callback function)
*/
void InvokeStartStreamingEvent();
/** Invokes the stop streaming event. This separate method is required, because it
* has to be started from the main thread. (It is used as callback function)
*/
void InvokeStopStreamingEvent();
private:
/**
* \brief a command that has to be executed in the main thread
*/
ProviderCommand::Pointer m_StreamingCommand;
ProviderCommand::Pointer m_StopStreamingCommand;
///**
// * \brief Timer thread for generating a continuous time signal for the stream
// *
// * Everyt time the time is passed a time signal is invoked.
// *
// * \param pInfoStruct pointer to the mitkIGTLMessageProvider object
// * \return
// */
//static ITK_THREAD_RETURN_TYPE TimerThread(void* pInfoStruct);
//int m_ThreadId;
///** \brief timer thread will terminate after the next wakeup if set to true */
//bool m_StopStreamingThread;
//itk::SmartPointer<itk::MultiThreader> m_MultiThreader;
/** \brief the time used for streaming */
unsigned int m_StreamingTime;
/** \brief mutex for guarding m_Time */
- itk::SmartPointer<itk::FastMutexLock> m_StreamingTimeMutex;
+ std::mutex m_StreamingTimeMutex;
///** \brief mutex for guarding m_StopStreamingThread */
//itk::SmartPointer<itk::FastMutexLock> m_StopStreamingThreadMutex;
/** \brief flag to indicate if the provider is streaming */
bool m_IsStreaming;
};
/**
* \brief connect to this Event to get notified when a stream is requested
*
* \note It is necessary to do the following things to have streaming support: 1. listen to this
* event. 2. When emitted start a timer with the given interval. 3. In the timeout method of
* this timer call IGTLMessageProvider::Update. 4. Also listen to the StreamingStopRequiredEvent
* and stop the timer imdediately.
* */
- itkEventMacro(StreamingStartRequiredEvent, itk::AnyEvent);
+ itkEventMacroDeclaration(StreamingStartRequiredEvent, itk::AnyEvent);
/**
* \brief connect to this Event to get notified when a stream shall be stopped
*
* \note It is necessary to connect to this event and stop the streaming timer when called.
* */
- itkEventMacro(StreamingStopRequiredEvent, itk::AnyEvent);
+ itkEventMacroDeclaration(StreamingStopRequiredEvent, itk::AnyEvent);
} // namespace mitk
#endif /* MITKIGTLMESSAGEPROVIDER_H_HEADER_INCLUDED_ */
diff --git a/Modules/OpenIGTLink/mitkIGTLMessageQueue.cpp b/Modules/OpenIGTLink/mitkIGTLMessageQueue.cpp
index ff5e0ab256..72857ac7ce 100644
--- a/Modules/OpenIGTLink/mitkIGTLMessageQueue.cpp
+++ b/Modules/OpenIGTLink/mitkIGTLMessageQueue.cpp
@@ -1,308 +1,307 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkIGTLMessageQueue.h"
#include <string>
#include "igtlMessageBase.h"
void mitk::IGTLMessageQueue::PushSendMessage(mitk::IGTLMessage::Pointer message)
{
- this->m_Mutex->Lock();
+ this->m_Mutex.lock();
if (this->m_BufferingType == IGTLMessageQueue::NoBuffering)
m_SendQueue.clear();
m_SendQueue.push_back(message);
- this->m_Mutex->Unlock();
+ this->m_Mutex.unlock();
}
void mitk::IGTLMessageQueue::PushCommandMessage(igtl::MessageBase::Pointer message)
{
- this->m_Mutex->Lock();
+ this->m_Mutex.lock();
if (this->m_BufferingType == IGTLMessageQueue::NoBuffering)
m_CommandQueue.clear();
m_CommandQueue.push_back(message);
- this->m_Mutex->Unlock();
+ this->m_Mutex.unlock();
}
void mitk::IGTLMessageQueue::PushMessage(igtl::MessageBase::Pointer msg)
{
- this->m_Mutex->Lock();
+ this->m_Mutex.lock();
std::stringstream infolog;
infolog << "Received message of type ";
if (dynamic_cast<igtl::TrackingDataMessage*>(msg.GetPointer()) != nullptr)
{
if (this->m_BufferingType == IGTLMessageQueue::NoBuffering)
m_TrackingDataQueue.clear();
this->m_TrackingDataQueue.push_back(dynamic_cast<igtl::TrackingDataMessage*>(msg.GetPointer()));
infolog << "TDATA";
}
else if (dynamic_cast<igtl::TransformMessage*>(msg.GetPointer()) != nullptr)
{
if (this->m_BufferingType == IGTLMessageQueue::NoBuffering)
m_TransformQueue.clear();
this->m_TransformQueue.push_back(dynamic_cast<igtl::TransformMessage*>(msg.GetPointer()));
infolog << "TRANSFORM";
}
else if (dynamic_cast<igtl::StringMessage*>(msg.GetPointer()) != nullptr)
{
if (this->m_BufferingType == IGTLMessageQueue::NoBuffering)
m_StringQueue.clear();
this->m_StringQueue.push_back(dynamic_cast<igtl::StringMessage*>(msg.GetPointer()));
infolog << "STRING";
}
else if (dynamic_cast<igtl::ImageMessage*>(msg.GetPointer()) != nullptr)
{
igtl::ImageMessage::Pointer imageMsg = dynamic_cast<igtl::ImageMessage*>(msg.GetPointer());
int* dim = new int[3];
imageMsg->GetDimensions(dim);
if (dim[2] > 1)
{
if (this->m_BufferingType == IGTLMessageQueue::NoBuffering)
m_Image3dQueue.clear();
this->m_Image3dQueue.push_back(dynamic_cast<igtl::ImageMessage*>(msg.GetPointer()));
infolog << "IMAGE3D";
}
else
{
if (this->m_BufferingType == IGTLMessageQueue::NoBuffering)
m_Image2dQueue.clear();
this->m_Image2dQueue.push_back(dynamic_cast<igtl::ImageMessage*>(msg.GetPointer()));
infolog << "IMAGE2D";
}
}
else
{
if (this->m_BufferingType == IGTLMessageQueue::NoBuffering)
m_MiscQueue.clear();
this->m_MiscQueue.push_back(msg);
infolog << "OTHER";
}
m_Latest_Message = msg;
//MITK_INFO << infolog.str();
- this->m_Mutex->Unlock();
+ this->m_Mutex.unlock();
}
mitk::IGTLMessage::Pointer mitk::IGTLMessageQueue::PullSendMessage()
{
mitk::IGTLMessage::Pointer ret = nullptr;
- this->m_Mutex->Lock();
+ this->m_Mutex.lock();
if (this->m_SendQueue.size() > 0)
{
ret = this->m_SendQueue.front();
this->m_SendQueue.pop_front();
}
- this->m_Mutex->Unlock();
+ this->m_Mutex.unlock();
return ret;
}
igtl::MessageBase::Pointer mitk::IGTLMessageQueue::PullMiscMessage()
{
igtl::MessageBase::Pointer ret = nullptr;
- this->m_Mutex->Lock();
+ this->m_Mutex.lock();
if (this->m_MiscQueue.size() > 0)
{
ret = this->m_MiscQueue.front();
this->m_MiscQueue.pop_front();
}
- this->m_Mutex->Unlock();
+ this->m_Mutex.unlock();
return ret;
}
igtl::ImageMessage::Pointer mitk::IGTLMessageQueue::PullImage2dMessage()
{
igtl::ImageMessage::Pointer ret = nullptr;
- this->m_Mutex->Lock();
+ this->m_Mutex.lock();
if (this->m_Image2dQueue.size() > 0)
{
ret = this->m_Image2dQueue.front();
this->m_Image2dQueue.pop_front();
}
- this->m_Mutex->Unlock();
+ this->m_Mutex.unlock();
return ret;
}
igtl::ImageMessage::Pointer mitk::IGTLMessageQueue::PullImage3dMessage()
{
igtl::ImageMessage::Pointer ret = nullptr;
- this->m_Mutex->Lock();
+ this->m_Mutex.lock();
if (this->m_Image3dQueue.size() > 0)
{
ret = this->m_Image3dQueue.front();
this->m_Image3dQueue.pop_front();
}
- this->m_Mutex->Unlock();
+ this->m_Mutex.unlock();
return ret;
}
igtl::TrackingDataMessage::Pointer mitk::IGTLMessageQueue::PullTrackingMessage()
{
igtl::TrackingDataMessage::Pointer ret = nullptr;
- this->m_Mutex->Lock();
+ this->m_Mutex.lock();
if (this->m_TrackingDataQueue.size() > 0)
{
ret = this->m_TrackingDataQueue.front();
this->m_TrackingDataQueue.pop_front();
}
- this->m_Mutex->Unlock();
+ this->m_Mutex.unlock();
return ret;
}
igtl::MessageBase::Pointer mitk::IGTLMessageQueue::PullCommandMessage()
{
igtl::MessageBase::Pointer ret = nullptr;
- this->m_Mutex->Lock();
+ this->m_Mutex.lock();
if (this->m_CommandQueue.size() > 0)
{
ret = this->m_CommandQueue.front();
this->m_CommandQueue.pop_front();
}
- this->m_Mutex->Unlock();
+ this->m_Mutex.unlock();
return ret;
}
igtl::StringMessage::Pointer mitk::IGTLMessageQueue::PullStringMessage()
{
igtl::StringMessage::Pointer ret = nullptr;
- this->m_Mutex->Lock();
+ this->m_Mutex.lock();
if (this->m_StringQueue.size() > 0)
{
ret = this->m_StringQueue.front();
this->m_StringQueue.pop_front();
}
- this->m_Mutex->Unlock();
+ this->m_Mutex.unlock();
return ret;
}
igtl::TransformMessage::Pointer mitk::IGTLMessageQueue::PullTransformMessage()
{
igtl::TransformMessage::Pointer ret = nullptr;
- this->m_Mutex->Lock();
+ this->m_Mutex.lock();
if (this->m_TransformQueue.size() > 0)
{
ret = this->m_TransformQueue.front();
this->m_TransformQueue.pop_front();
}
- this->m_Mutex->Unlock();
+ this->m_Mutex.unlock();
return ret;
}
std::string mitk::IGTLMessageQueue::GetNextMsgInformationString()
{
- this->m_Mutex->Lock();
+ this->m_Mutex.lock();
std::stringstream s;
if (this->m_Latest_Message != nullptr)
{
s << "Device Type: " << this->m_Latest_Message->GetDeviceType() << std::endl;
s << "Device Name: " << this->m_Latest_Message->GetDeviceName() << std::endl;
}
else
{
s << "No Msg";
}
- this->m_Mutex->Unlock();
+ this->m_Mutex.unlock();
return s.str();
}
std::string mitk::IGTLMessageQueue::GetNextMsgDeviceType()
{
- this->m_Mutex->Lock();
+ this->m_Mutex.lock();
std::stringstream s;
if (m_Latest_Message != nullptr)
{
s << this->m_Latest_Message->GetDeviceType();
}
else
{
s << "";
}
- this->m_Mutex->Unlock();
+ this->m_Mutex.unlock();
return s.str();
}
std::string mitk::IGTLMessageQueue::GetLatestMsgInformationString()
{
- this->m_Mutex->Lock();
+ this->m_Mutex.lock();
std::stringstream s;
if (m_Latest_Message != nullptr)
{
s << "Device Type: " << this->m_Latest_Message->GetDeviceType() << std::endl;
s << "Device Name: " << this->m_Latest_Message->GetDeviceName() << std::endl;
}
else
{
s << "No Msg";
}
- this->m_Mutex->Unlock();
+ this->m_Mutex.unlock();
return s.str();
}
std::string mitk::IGTLMessageQueue::GetLatestMsgDeviceType()
{
- this->m_Mutex->Lock();
+ this->m_Mutex.lock();
std::stringstream s;
if (m_Latest_Message != nullptr)
{
s << this->m_Latest_Message->GetDeviceType();
}
else
{
s << "";
}
- this->m_Mutex->Unlock();
+ this->m_Mutex.unlock();
return s.str();
}
int mitk::IGTLMessageQueue::GetSize()
{
return (this->m_CommandQueue.size() + this->m_Image2dQueue.size() + this->m_Image3dQueue.size() + this->m_MiscQueue.size()
+ this->m_StringQueue.size() + this->m_TrackingDataQueue.size() + this->m_TransformQueue.size());
}
void mitk::IGTLMessageQueue::EnableNoBufferingMode(bool enable)
{
- this->m_Mutex->Lock();
+ this->m_Mutex.lock();
if (enable)
this->m_BufferingType = IGTLMessageQueue::BufferingType::NoBuffering;
else
this->m_BufferingType = IGTLMessageQueue::BufferingType::Infinit;
- this->m_Mutex->Unlock();
+ this->m_Mutex.unlock();
}
mitk::IGTLMessageQueue::IGTLMessageQueue()
{
- this->m_Mutex = itk::FastMutexLock::New();
this->m_BufferingType = IGTLMessageQueue::NoBuffering;
}
mitk::IGTLMessageQueue::~IGTLMessageQueue()
{
- this->m_Mutex->Unlock();
+ this->m_Mutex.unlock();
}
diff --git a/Modules/OpenIGTLink/mitkIGTLMessageQueue.h b/Modules/OpenIGTLink/mitkIGTLMessageQueue.h
index 0863ecc23a..b33094565c 100644
--- a/Modules/OpenIGTLink/mitkIGTLMessageQueue.h
+++ b/Modules/OpenIGTLink/mitkIGTLMessageQueue.h
@@ -1,139 +1,139 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef IGTLMessageQueue_H
#define IGTLMessageQueue_H
#include "MitkOpenIGTLinkExports.h"
#include "itkObject.h"
-#include "itkFastMutexLock.h"
#include "mitkCommon.h"
#include <deque>
+#include <mutex>
#include <mitkIGTLMessage.h>
//OpenIGTLink
#include "igtlMessageBase.h"
#include "igtlImageMessage.h"
#include "igtlStringMessage.h"
#include "igtlTrackingDataMessage.h"
#include "igtlTransformMessage.h"
namespace mitk {
/**
* \class IGTLMessageQueue
* \brief Thread safe message queue to store OpenIGTLink messages.
*
* \ingroup OpenIGTLink
*/
class MITKOPENIGTLINK_EXPORT IGTLMessageQueue : public itk::Object
{
public:
mitkClassMacroItkParent(mitk::IGTLMessageQueue, itk::Object);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* \brief Different buffering types
* Infinit buffering means that you can push as many messages as you want
* NoBuffering means that the queue just stores a single message
*/
enum BufferingType { Infinit, NoBuffering };
void PushSendMessage(mitk::IGTLMessage::Pointer message);
/**
* \brief Adds the message to the queue
*/
void PushMessage(igtl::MessageBase::Pointer message);
/**
* \brief Adds the message to the queue
*/
void PushCommandMessage(igtl::MessageBase::Pointer message);
/**
* \brief Returns and removes the oldest message from the queue
*/
igtl::MessageBase::Pointer PullMiscMessage();
igtl::ImageMessage::Pointer PullImage2dMessage();
igtl::ImageMessage::Pointer PullImage3dMessage();
igtl::TrackingDataMessage::Pointer PullTrackingMessage();
igtl::MessageBase::Pointer PullCommandMessage();
igtl::StringMessage::Pointer PullStringMessage();
igtl::TransformMessage::Pointer PullTransformMessage();
mitk::IGTLMessage::Pointer PullSendMessage();
/**
* \brief Get the number of messages in the queue
*/
int GetSize();
/**
* \brief Returns a string with information about the oldest message in the
* queue
*/
std::string GetNextMsgInformationString();
/**
* \brief Returns the device type of the oldest message in the queue
*/
std::string GetNextMsgDeviceType();
/**
* \brief Returns a string with information about the oldest message in the
* queue
*/
std::string GetLatestMsgInformationString();
/**
* \brief Returns the device type of the oldest message in the queue
*/
std::string GetLatestMsgDeviceType();
/**
*/
void EnableNoBufferingMode(bool enable);
protected:
IGTLMessageQueue();
~IGTLMessageQueue() override;
protected:
/**
* \brief Mutex to take car of the queue
*/
- itk::FastMutexLock::Pointer m_Mutex;
+ std::mutex m_Mutex;
/**
* \brief the queue that stores pointer to the inserted messages
*/
std::deque< igtl::MessageBase::Pointer > m_CommandQueue;
std::deque< igtl::ImageMessage::Pointer > m_Image2dQueue;
std::deque< igtl::ImageMessage::Pointer > m_Image3dQueue;
std::deque< igtl::TransformMessage::Pointer > m_TransformQueue;
std::deque< igtl::TrackingDataMessage::Pointer > m_TrackingDataQueue;
std::deque< igtl::StringMessage::Pointer > m_StringQueue;
std::deque< igtl::MessageBase::Pointer > m_MiscQueue;
std::deque< mitk::IGTLMessage::Pointer > m_SendQueue;
igtl::MessageBase::Pointer m_Latest_Message;
/**
* \brief defines the kind of buffering
*/
BufferingType m_BufferingType;
};
}
#endif
diff --git a/Modules/OpenIGTLink/mitkIGTLMessageSource.cpp b/Modules/OpenIGTLink/mitkIGTLMessageSource.cpp
index f42dedd4dd..37c3d29deb 100644
--- a/Modules/OpenIGTLink/mitkIGTLMessageSource.cpp
+++ b/Modules/OpenIGTLink/mitkIGTLMessageSource.cpp
@@ -1,198 +1,197 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkIGTLMessageSource.h"
#include "mitkUIDGenerator.h"
//Microservices
#include <usGetModuleContext.h>
#include <usModule.h>
#include <usServiceProperties.h>
#include <usModuleContext.h>
const std::string mitk::IGTLMessageSource::US_INTERFACE_NAME =
"org.mitk.services.IGTLMessageSource";
const std::string mitk::IGTLMessageSource::US_PROPKEY_DEVICENAME =
US_INTERFACE_NAME + ".devicename";
const std::string mitk::IGTLMessageSource::US_PROPKEY_DEVICETYPE =
US_INTERFACE_NAME + ".devicetype";
const std::string mitk::IGTLMessageSource::US_PROPKEY_ID =
US_INTERFACE_NAME + ".id";
const std::string mitk::IGTLMessageSource::US_PROPKEY_ISACTIVE =
US_INTERFACE_NAME + ".isActive";
mitk::IGTLMessageSource::IGTLMessageSource()
: itk::ProcessObject(), m_Name("IGTLMessageSource (no defined type)"),
m_Type("NONE"), m_StreamingFPS(0)
{
- m_StreamingFPSMutex = itk::FastMutexLock::New();
}
mitk::IGTLMessageSource::~IGTLMessageSource()
{
//this->UnRegisterMicroservice();
}
mitk::IGTLMessage* mitk::IGTLMessageSource::GetOutput()
{
if (this->GetNumberOfIndexedOutputs() < 1)
{
MITK_WARN << "IGTLMessageSource contained no outputs. Returning nullptr.";
return nullptr;
}
return static_cast<IGTLMessage*>(this->ProcessObject::GetPrimaryOutput());
}
mitk::IGTLMessage* mitk::IGTLMessageSource::GetOutput(
DataObjectPointerArraySizeType idx)
{
IGTLMessage* out =
dynamic_cast<IGTLMessage*>( this->ProcessObject::GetOutput(idx) );
if ( out == nullptr && this->ProcessObject::GetOutput(idx) != nullptr )
{
itkWarningMacro (<< "Unable to convert output number " << idx << " to type "
<< typeid( IGTLMessage ).name () );
}
return out;
}
mitk::IGTLMessage* mitk::IGTLMessageSource::GetOutput(
const std::string& messageName)
{
DataObjectPointerArray outputs = this->GetOutputs();
for (DataObjectPointerArray::iterator it = outputs.begin();
it != outputs.end();
++it)
{
if (messageName ==
(static_cast<IGTLMessage*>(it->GetPointer()))->GetName())
{
return static_cast<IGTLMessage*>(it->GetPointer());
}
}
return nullptr;
}
itk::ProcessObject::DataObjectPointerArraySizeType
mitk::IGTLMessageSource::GetOutputIndex( std::string messageName )
{
DataObjectPointerArray outputs = this->GetOutputs();
for (DataObjectPointerArray::size_type i = 0; i < outputs.size(); ++i)
{
if (messageName ==
(static_cast<IGTLMessage*>(outputs.at(i).GetPointer()))->GetName())
{
return i;
}
}
throw std::invalid_argument("output name does not exist");
}
void mitk::IGTLMessageSource::RegisterAsMicroservice()
{
// Get Context
us::ModuleContext* context = us::GetModuleContext();
// Define ServiceProps
us::ServiceProperties props;
mitk::UIDGenerator uidGen =
mitk::UIDGenerator ("org.mitk.services.IGTLMessageSource.id_");
props[ US_PROPKEY_ID ] = uidGen.GetUID();
props[ US_PROPKEY_DEVICENAME ] = m_Name;
props[ US_PROPKEY_DEVICETYPE ] = m_Type;
m_ServiceRegistration = context->RegisterService(this, props);
}
void mitk::IGTLMessageSource::UnRegisterMicroservice()
{
if (m_ServiceRegistration != nullptr)
{
m_ServiceRegistration.Unregister();
}
m_ServiceRegistration = 0;
}
std::string mitk::IGTLMessageSource::GetMicroserviceID()
{
us::Any referenceProperty =
this->m_ServiceRegistration.GetReference().GetProperty(US_PROPKEY_ID);
return referenceProperty.ToString();
}
void mitk::IGTLMessageSource::GraftOutput(itk::DataObject *graft)
{
this->GraftNthOutput(0, graft);
}
void mitk::IGTLMessageSource::GraftNthOutput(unsigned int idx,
itk::DataObject *graft)
{
if ( idx >= this->GetNumberOfIndexedOutputs() )
{
itkExceptionMacro(<<"Requested to graft output " << idx << " but this filter"
"only has " << this->GetNumberOfIndexedOutputs() << " Outputs.");
}
if ( !graft )
{
itkExceptionMacro(<<"Requested to graft output with a nullptr pointer object" );
}
itk::DataObject* output = this->GetOutput(idx);
if ( !output )
{
itkExceptionMacro(<<"Requested to graft output that is a nullptr pointer" );
}
// Call Graft on IGTLMessage to copy member data
output->Graft( graft );
}
itk::DataObject::Pointer mitk::IGTLMessageSource::MakeOutput ( DataObjectPointerArraySizeType /*idx*/ )
{
return IGTLMessage::New().GetPointer();
}
itk::DataObject::Pointer mitk::IGTLMessageSource::MakeOutput( const DataObjectIdentifierType & name )
{
itkDebugMacro("MakeOutput(" << name << ")");
if( this->IsIndexedOutputName(name) )
{
return this->MakeOutput( this->MakeIndexFromOutputName(name) );
}
return static_cast<itk::DataObject *>(IGTLMessage::New().GetPointer());
}
mitk::PropertyList::ConstPointer mitk::IGTLMessageSource::GetParameters() const
{
mitk::PropertyList::Pointer p = mitk::PropertyList::New();
// add properties to p like this:
//p->SetProperty("MyFilter_MyParameter", mitk::PropertyDataType::New(m_MyParameter));
return mitk::PropertyList::ConstPointer(p);
}
void mitk::IGTLMessageSource::SetFPS(unsigned int fps)
{
- this->m_StreamingFPSMutex->Lock();
+ this->m_StreamingFPSMutex.lock();
this->m_StreamingFPS = fps;
- this->m_StreamingFPSMutex->Unlock();
+ this->m_StreamingFPSMutex.unlock();
}
unsigned int mitk::IGTLMessageSource::GetFPS()
{
unsigned int fps = 0;
- this->m_StreamingFPSMutex->Lock();
+ this->m_StreamingFPSMutex.lock();
fps = this->m_StreamingFPS;
- this->m_StreamingFPSMutex->Unlock();
+ this->m_StreamingFPSMutex.unlock();
return fps;
}
diff --git a/Modules/OpenIGTLink/mitkIGTLMessageSource.h b/Modules/OpenIGTLink/mitkIGTLMessageSource.h
index 2c3e4ac29e..320c66b005 100644
--- a/Modules/OpenIGTLink/mitkIGTLMessageSource.h
+++ b/Modules/OpenIGTLink/mitkIGTLMessageSource.h
@@ -1,208 +1,207 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKIGTLMESSAGESOURCE_H_HEADER_INCLUDED_
#define MITKIGTLMESSAGESOURCE_H_HEADER_INCLUDED_
#include <itkProcessObject.h>
#include "mitkPropertyList.h"
#include "MitkOpenIGTLinkExports.h"
#include "mitkIGTLMessage.h"
// Microservices
#include <mitkServiceInterface.h>
#include <usServiceRegistration.h>
-//itk
-#include <itkFastMutexLock.h>
+#include <mutex>
namespace mitk {
/**
* \brief OpenIGTLink message source
*
* Base class for all OpenIGTLink filters that produce OpenIGTLink message
* objects as output. This class defines the output-interface for
* OpenIGTLinkMessageFilters.
* \warning: if Update() is called on any output object, all IGTLMessage filters
* will generate new output data for all outputs, not just the one on which
* Update() was called.
*
*/
class MITKOPENIGTLINK_EXPORT IGTLMessageSource : public itk::ProcessObject
{
public:
mitkClassMacroItkParent(IGTLMessageSource, itk::ProcessObject);
/** @return Returns a human readable name of this source. There will be a
* default name, or you can set the name with the method SetName() if you
* want to change it.
*/
itkGetMacro(Name,std::string);
/** @brief Sets the human readable name of this source. There is also a
* default name, but you can use this method if you need to define it on your
* own.
*/
itkSetMacro(Name,std::string);
/** @return Returns a human readable type of this source. There will be a
* default type, or you can set the name with the method SetType(). You have
* to set this parameter otherwise it will not be found by the message
* provider.
*/
itkGetMacro(Type,std::string);
/** @return Returns a human readable type of this source. There will be a
* default type, or you can set the name with the method SetType(). You have
* to set this parameter otherwise it will not be found by the message
* provider.
*/
itkSetMacro(Type,std::string);
/**
*\brief return the output (output with id 0) of the filter
*/
IGTLMessage* GetOutput(void);
/**
*\brief return the output with id idx of the filter
*/
IGTLMessage* GetOutput(DataObjectPointerArraySizeType idx);
/**
*\brief return the output with name messageName of the filter
*/
IGTLMessage* GetOutput(const std::string& messageName);
/**
*\brief return the index of the output with name messageName, -1 if no output
* with that name was found
*
* \warning if a subclass has outputs that have different data type than
* igtl::MessageBase, they have to overwrite this method
*/
DataObjectPointerArraySizeType GetOutputIndex(std::string messageName);
/**
*\brief Registers this object as a Microservice, making it available to every
* module and/or plugin. To unregister, call UnregisterMicroservice().
*/
virtual void RegisterAsMicroservice();
/**
*\brief Registers this object as a Microservice, making it available to every
* module and/or plugin.
*/
virtual void UnRegisterMicroservice();
/**
*\brief Returns the id that this device is registered with. The id will only
* be valid, if the IGTLMessageSource has been registered using
* RegisterAsMicroservice().
*/
std::string GetMicroserviceID();
/**
*\brief These Constants are used in conjunction with Microservices
*/
static const std::string US_INTERFACE_NAME;
static const std::string US_PROPKEY_DEVICENAME;
static const std::string US_PROPKEY_DEVICETYPE;
static const std::string US_PROPKEY_ID;
static const std::string US_PROPKEY_ISACTIVE; //NOT IMPLEMENTED YET!
/**
*\brief Graft the specified DataObject onto this ProcessObject's output.
*
* See itk::ImageSource::GraftNthOutput for details
*/
virtual void GraftNthOutput(unsigned int idx, itk::DataObject *graft);
/**
* \brief Graft the specified DataObject onto this ProcessObject's output.
*
* See itk::ImageSource::Graft Output for details
*/
virtual void GraftOutput(itk::DataObject *graft);
/**
* Allocates a new output object and returns it. Currently the
* index idx is not evaluated.
* @param idx the index of the output for which an object should be created
* @returns the new object
*/
itk::DataObject::Pointer MakeOutput ( DataObjectPointerArraySizeType idx ) override;
/**
* This is a default implementation to make sure we have something.
* Once all the subclasses of ProcessObject provide an appopriate
* MakeOutput(), then ProcessObject::MakeOutput() can be made pure
* virtual.
*/
itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override;
/**
* \brief Set all filter parameters as the PropertyList p
*
* This method allows to set all parameters of a filter with one
* method call. For the names of the parameters, take a look at
* the GetParameters method of the filter
* This method has to be overwritten by each MITK-IGT filter.
*/
virtual void SetParameters(const mitk::PropertyList*){};
/**
* \brief Get all filter parameters as a PropertyList
*
* This method allows to get all parameters of a filter with one
* method call. The returned PropertyList must be assigned to a
* SmartPointer immediately, or else it will get destroyed.
* Every filter must overwrite this method to create a filter-specific
* PropertyList. Note that property names must be unique over all
* MITK-IGT filters. Therefore each filter should use its name as a prefix
* for each property name.
* Secondly, each filter should list the property names and data types
* in the method documentation.
*/
virtual mitk::PropertyList::ConstPointer GetParameters() const;
/**
*\brief Sets the fps used for streaming this source
*/
void SetFPS(unsigned int fps);
/**
*\brief Gets the fps used for streaming this source
*/
unsigned int GetFPS();
protected:
IGTLMessageSource();
~IGTLMessageSource() override;
std::string m_Name;
std::string m_Type;
/** mutex to control access to m_StreamingFPS */
- itk::FastMutexLock::Pointer m_StreamingFPSMutex;
+ std::mutex m_StreamingFPSMutex;
/** The frames per second used for streaming */
unsigned int m_StreamingFPS;
us::ServiceRegistration<Self> m_ServiceRegistration;
};
} // namespace mitk
// This is the microservice declaration. Do not meddle!
MITK_DECLARE_SERVICE_INTERFACE(mitk::IGTLMessageSource, "org.mitk.services.IGTLMessageSource")
#endif /* MITKIGTLMESSAGESOURCE_H_HEADER_INCLUDED_ */
diff --git a/Modules/OpenIGTLink/mitkIGTLServer.cpp b/Modules/OpenIGTLink/mitkIGTLServer.cpp
index 817b37c3e3..e18c9ab5be 100644
--- a/Modules/OpenIGTLink/mitkIGTLServer.cpp
+++ b/Modules/OpenIGTLink/mitkIGTLServer.cpp
@@ -1,212 +1,207 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkIGTLServer.h"
#include <cstdio>
#include <itksys/SystemTools.hxx>
-#include <itkMutexLockHolder.h>
#include <igtlServerSocket.h>
#include <igtlTrackingDataMessage.h>
#include <igtlImageMessage.h>
#include <igtl_status.h>
mitk::IGTLServer::IGTLServer(bool ReadFully) :
IGTLDevice(ReadFully)
{
- m_ReceiveListMutex = itk::FastMutexLock::New();
- m_SentListMutex = itk::FastMutexLock::New();
}
mitk::IGTLServer::~IGTLServer()
{
- m_ReceiveListMutex = nullptr;
- m_SentListMutex = nullptr;
}
bool mitk::IGTLServer::OpenConnection()
{
if (this->GetState() != Setup)
{
mitkThrowException(mitk::Exception) <<
"Can only try to create a server if in setup mode";
return false;
}
int portNumber = this->GetPortNumber();
if (portNumber == -1)
{
//port number was not correct
return false;
}
//create a new server socket
m_Socket = igtl::ServerSocket::New();
//try to create the igtl server
int response = dynamic_cast<igtl::ServerSocket*>(m_Socket.GetPointer())->
CreateServer(portNumber);
//check the response
if (response != 0)
{
mitkThrowException(mitk::Exception) <<
"The server could not be created. Port: " << portNumber;
return false;
}
// everything is initialized and connected so the communication can be started
this->SetState(Ready);
return true;
}
bool mitk::IGTLServer::CloseConnection()
{
//remove all registered clients
- m_SentListMutex->Lock();
- m_ReceiveListMutex->Lock();
+ m_SentListMutex.lock();
+ m_ReceiveListMutex.lock();
SocketListType allRegisteredSockets(m_RegisteredClients);
- m_SentListMutex->Unlock();
- m_ReceiveListMutex->Unlock();
+ m_SentListMutex.unlock();
+ m_ReceiveListMutex.unlock();
this->StopCommunicationWithSocket(allRegisteredSockets);
return mitk::IGTLDevice::CloseConnection();
}
void mitk::IGTLServer::Connect()
{
igtl::Socket::Pointer socket;
//check if another igtl device wants to connect to this socket
socket =
((igtl::ServerSocket*)(this->m_Socket.GetPointer()))->WaitForConnection(1);
//if there is a new connection the socket is not null
if (socket.IsNotNull())
{
//add the new client socket to the list of registered clients
- m_SentListMutex->Lock();
- m_ReceiveListMutex->Lock();
+ m_SentListMutex.lock();
+ m_ReceiveListMutex.lock();
this->m_RegisteredClients.push_back(socket);
- m_SentListMutex->Unlock();
- m_ReceiveListMutex->Unlock();
+ m_SentListMutex.unlock();
+ m_ReceiveListMutex.unlock();
//inform observers about this new client
this->InvokeEvent(NewClientConnectionEvent());
MITK_INFO("IGTLServer") << "Connected to a new client: " << socket;
}
}
void mitk::IGTLServer::Receive()
{
unsigned int status = IGTL_STATUS_OK;
SocketListType socketsToBeRemoved;
//the server can be connected with several clients, therefore it has to check
//all registered clients
SocketListIteratorType it;
- m_ReceiveListMutex->Lock();
+ m_ReceiveListMutex.lock();
auto it_end = this->m_RegisteredClients.end();
for (it = this->m_RegisteredClients.begin(); it != it_end; ++it)
{
//it is possible that ReceivePrivate detects that the current socket is
//already disconnected. Therefore, it is necessary to remove this socket
//from the registered clients list
status = this->ReceivePrivate(*it);
if (status == IGTL_STATUS_NOT_PRESENT)
{
//remember this socket for later, it is not a good idea to remove it
//from the list directly because we iterate over the list at this point
socketsToBeRemoved.push_back(*it);
MITK_WARN("IGTLServer") << "Lost connection to a client socket. ";
}
else if (status != 1)
{
MITK_DEBUG("IGTLServer") << "IGTL Message with status: " << status;
}
}
- m_ReceiveListMutex->Unlock();
+ m_ReceiveListMutex.unlock();
if (socketsToBeRemoved.size() > 0)
{
//remove the sockets that are not connected anymore
this->StopCommunicationWithSocket(socketsToBeRemoved);
//inform observers about loosing the connection to these sockets
this->InvokeEvent(LostConnectionEvent());
}
}
void mitk::IGTLServer::Send()
{
//get the latest message from the queue
mitk::IGTLMessage::Pointer curMessage = this->m_MessageQueue->PullSendMessage();
// there is no message => return
if (curMessage.IsNull())
return;
//the server can be connected with several clients, therefore it has to check
//all registered clients
//sending a message to all registered clients might not be the best solution,
//it could be better to store the client together with the requested type. Then
//the data would be send to the appropriate client and to noone else.
//(I know it is no excuse but PLUS is doing exactly the same, they broadcast
//everything)
- m_SentListMutex->Lock();
+ m_SentListMutex.lock();
SocketListIteratorType it;
auto it_end =
this->m_RegisteredClients.end();
for (it = this->m_RegisteredClients.begin(); it != it_end; ++it)
{
//maybe there should be a check here if the current socket is still active
this->SendMessagePrivate(curMessage, *it);
MITK_DEBUG("IGTLServer") << "Sent IGTL Message";
}
- m_SentListMutex->Unlock();
+ m_SentListMutex.unlock();
}
void mitk::IGTLServer::StopCommunicationWithSocket(
SocketListType& toBeRemovedSockets)
{
for (auto i = toBeRemovedSockets.begin(); i != toBeRemovedSockets.end(); i++)
this->StopCommunicationWithSocket(*i);
}
void mitk::IGTLServer::StopCommunicationWithSocket(igtl::Socket* client)
{
- m_SentListMutex->Lock();
- m_ReceiveListMutex->Lock();
+ m_SentListMutex.lock();
+ m_ReceiveListMutex.lock();
auto i = m_RegisteredClients.begin();
auto end = m_RegisteredClients.end();
while (i != end)
{
if ((*i) == client)
{
// //close the socket
(*i)->CloseSocket();
//and remove it from the list
i = this->m_RegisteredClients.erase(i);
MITK_INFO("IGTLServer") << "Removed client socket from server client list.";
break;
}
else
{
++i;
}
}
- m_SentListMutex->Unlock();
- m_ReceiveListMutex->Unlock();
+ m_SentListMutex.unlock();
+ m_ReceiveListMutex.unlock();
}
unsigned int mitk::IGTLServer::GetNumberOfConnections()
{
return this->m_RegisteredClients.size();
}
diff --git a/Modules/OpenIGTLink/mitkIGTLServer.h b/Modules/OpenIGTLink/mitkIGTLServer.h
index 62a085d328..75b4608273 100644
--- a/Modules/OpenIGTLink/mitkIGTLServer.h
+++ b/Modules/OpenIGTLink/mitkIGTLServer.h
@@ -1,124 +1,124 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKIGTLSERVER_H
#define MITKIGTLSERVER_H
#include "mitkIGTLDevice.h"
#include <MitkOpenIGTLinkExports.h>
namespace mitk
{
/**
* \brief Superclass for OpenIGTLink server
*
* Implements the IGTLDevice interface for IGTLServers. In certain points it
* behaves different than the IGTLClient. The client connects directly to a
* server (it cannot connect to two different servers) while the server can
* connect to several clients. Therefore, it is necessary for the server to
* have a list with registered sockets.
*
* \ingroup OpenIGTLink
*/
class MITKOPENIGTLINK_EXPORT IGTLServer : public IGTLDevice
{
public:
mitkClassMacro(IGTLServer, IGTLDevice);
mitkNewMacro1Param(Self, bool);
itkCloneMacro(Self);
typedef std::list<igtl::Socket::Pointer> SocketListType;
typedef SocketListType::iterator SocketListIteratorType;
/**
* \brief Initialize the connection for the IGTLServer
*
*
* OpenConnection() starts the IGTLServer socket so that clients can connect
* to it.
* @throw mitk::Exception Throws an exception if the given port is occupied.
*/
bool OpenConnection() override;
/**
* \brief Closes the connection to the device
*
* This may only be called if there is currently a connection to the
* device, but device is not running (e.g. object is in Ready state)
*/
bool CloseConnection() override;
/**
* \brief Returns the number of client connections of this device
*/
unsigned int GetNumberOfConnections() override;
protected:
/** Constructor */
IGTLServer(bool ReadFully);
/** Destructor */
~IGTLServer() override;
/**
* \brief Call this method to check for other devices that want to connect
* to this one.
*
* In case of a client this method is doing nothing. In case of a server it
* is checking for other devices and if there is one it establishes a
* connection and adds the socket to m_RegisteredClients.
*/
void Connect() override;
/**
* \brief Call this method to receive a message.
*
* The message will be saved in the receive queue.
*/
void Receive() override;
/**
* \brief Call this method to send a message.
* The message will be read from the queue. So far the message is send to all
* connected sockets (broadcast).
*/
void Send() override;
/**
* \brief Stops the communication with the given sockets.
*
* This method removes the given sockets from the registered clients list
*
*/
virtual void StopCommunicationWithSocket(SocketListType& toBeRemovedSockets);
/**
* \brief Stops the communication with the given socket.
*
* This method removes the given socket from the registered clients list
*
*/
void StopCommunicationWithSocket(igtl::Socket* client) override;
/**
* \brief A list with all registered clients
*/
SocketListType m_RegisteredClients;
/** mutex to control access to m_RegisteredClients */
- itk::FastMutexLock::Pointer m_ReceiveListMutex;
+ std::mutex m_ReceiveListMutex;
/** mutex to control access to m_RegisteredClients */
- itk::FastMutexLock::Pointer m_SentListMutex;
+ std::mutex m_SentListMutex;
};
} // namespace mitk
#endif /* MITKIGTLSERVER_H */
diff --git a/Modules/PlanarFigure/include/mitkPlanarFigureInteractor.h b/Modules/PlanarFigure/include/mitkPlanarFigureInteractor.h
index 63a3f33642..84a3f8892a 100644
--- a/Modules/PlanarFigure/include/mitkPlanarFigureInteractor.h
+++ b/Modules/PlanarFigure/include/mitkPlanarFigureInteractor.h
@@ -1,198 +1,198 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKPLANARFIGUREINTERACTOR_H
#define MITKPLANARFIGUREINTERACTOR_H
#include <MitkPlanarFigureExports.h>
#include "mitkCommon.h"
#include "mitkDataInteractor.h"
#include "mitkNumericTypes.h"
#pragma GCC visibility push(default)
#include <itkEventObject.h>
#pragma GCC visibility pop
namespace mitk
{
class DataNode;
class PlaneGeometry;
class PlanarFigure;
class PositionEvent;
class BaseRenderer;
class InteractionPositionEvent;
class StateMachineAction;
#pragma GCC visibility push(default)
// Define events for PlanarFigure interaction notifications
- itkEventMacro(PlanarFigureEvent, itk::AnyEvent);
- itkEventMacro(StartPlacementPlanarFigureEvent, PlanarFigureEvent);
- itkEventMacro(EndPlacementPlanarFigureEvent, PlanarFigureEvent);
- itkEventMacro(SelectPlanarFigureEvent, PlanarFigureEvent);
- itkEventMacro(StartInteractionPlanarFigureEvent, PlanarFigureEvent);
- itkEventMacro(EndInteractionPlanarFigureEvent, PlanarFigureEvent);
- itkEventMacro(StartHoverPlanarFigureEvent, PlanarFigureEvent);
- itkEventMacro(EndHoverPlanarFigureEvent, PlanarFigureEvent);
- itkEventMacro(ContextMenuPlanarFigureEvent, PlanarFigureEvent);
- itkEventMacro(PointMovedPlanarFigureEvent, PlanarFigureEvent);
+ itkEventMacroDeclaration(PlanarFigureEvent, itk::AnyEvent);
+ itkEventMacroDeclaration(StartPlacementPlanarFigureEvent, PlanarFigureEvent);
+ itkEventMacroDeclaration(EndPlacementPlanarFigureEvent, PlanarFigureEvent);
+ itkEventMacroDeclaration(SelectPlanarFigureEvent, PlanarFigureEvent);
+ itkEventMacroDeclaration(StartInteractionPlanarFigureEvent, PlanarFigureEvent);
+ itkEventMacroDeclaration(EndInteractionPlanarFigureEvent, PlanarFigureEvent);
+ itkEventMacroDeclaration(StartHoverPlanarFigureEvent, PlanarFigureEvent);
+ itkEventMacroDeclaration(EndHoverPlanarFigureEvent, PlanarFigureEvent);
+ itkEventMacroDeclaration(ContextMenuPlanarFigureEvent, PlanarFigureEvent);
+ itkEventMacroDeclaration(PointMovedPlanarFigureEvent, PlanarFigureEvent);
#pragma GCC visibility pop
/**
* \brief Interaction with mitk::PlanarFigure objects via control-points
*
* @ingroup MitkPlanarFigureModule
*/
class MITKPLANARFIGURE_EXPORT PlanarFigureInteractor : public DataInteractor
{
public:
mitkClassMacro(PlanarFigureInteractor, DataInteractor);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** \brief Sets the amount of precision */
void SetPrecision(ScalarType precision);
/** \brief Sets the minimal distance between two control points. */
void SetMinimumPointDistance(ScalarType minimumDistance);
protected:
PlanarFigureInteractor();
~PlanarFigureInteractor() override;
void ConnectActionsAndFunctions() override;
//////// Conditions ////////
bool CheckFigurePlaced(const InteractionEvent *interactionEvent);
bool CheckFigureHovering(const InteractionEvent *interactionEvent);
bool CheckControlPointHovering(const InteractionEvent *interactionEvent);
bool CheckSelection(const InteractionEvent *interactionEvent);
bool CheckPointValidity(const InteractionEvent *interactionEvent);
bool CheckFigureFinished(const InteractionEvent *interactionEvent);
bool CheckResetOnPointSelect(const InteractionEvent *interactionEvent);
bool CheckFigureOnRenderingGeometry(const InteractionEvent *interactionEvent);
bool CheckMinimalFigureFinished(const InteractionEvent *interactionEvent);
bool CheckFigureIsExtendable(const InteractionEvent *interactionEvent);
bool CheckFigureIsDeletable(const InteractionEvent *interactionEvent);
bool CheckFigureIsEditable(const InteractionEvent *interactionEvent);
//////// Actions ////////
void FinalizeFigure(StateMachineAction *, InteractionEvent *interactionEvent);
void MoveCurrentPoint(StateMachineAction *, InteractionEvent *interactionEvent);
void DeselectPoint(StateMachineAction *, InteractionEvent *interactionEvent);
void AddPoint(StateMachineAction *, InteractionEvent *interactionEvent);
void AddInitialPoint(StateMachineAction *, InteractionEvent *interactionEvent);
void StartHovering(StateMachineAction *, InteractionEvent *interactionEvent);
void EndHovering(StateMachineAction *, InteractionEvent *interactionEvent);
void DeleteFigure(StateMachineAction *, InteractionEvent *interactionEvent);
void PerformPointResetOnSelect(StateMachineAction *, InteractionEvent *interactionEvent);
void SetPreviewPointPosition(StateMachineAction *, InteractionEvent *interactionEvent);
void HidePreviewPoint(StateMachineAction *, InteractionEvent *interactionEvent);
void HideControlPoints(StateMachineAction *, InteractionEvent *interactionEvent);
void RemoveSelectedPoint(StateMachineAction *, InteractionEvent *interactionEvent);
void RequestContextMenu(StateMachineAction *, InteractionEvent *interactionEvent);
void SelectFigure(StateMachineAction *, InteractionEvent *interactionEvent);
void SelectPoint(StateMachineAction *, InteractionEvent *interactionEvent);
void EndInteraction(StateMachineAction *, InteractionEvent *interactionEvent);
bool FilterEvents(InteractionEvent *interactionEvent, DataNode *) override;
/**
\brief Used when clicking to determine if a point is too close to the previous point.
*/
bool IsMousePositionAcceptableAsNewControlPoint(const mitk::InteractionPositionEvent *positionEvent,
const PlanarFigure *);
bool TransformPositionEventToPoint2D(const InteractionPositionEvent *positionEvent,
const PlaneGeometry *planarFigureGeometry,
Point2D &point2D);
bool TransformObjectToDisplay(const mitk::Point2D &point2D,
mitk::Point2D &displayPoint,
const mitk::PlaneGeometry *objectGeometry,
const mitk::PlaneGeometry *rendererGeometry,
const mitk::BaseRenderer *renderer) const;
/** \brief Returns true if the first specified point is in proximity of the line defined
* the other two point; false otherwise.
*
* Proximity is defined as the rectangle around the line with pre-defined distance
* from the line. */
bool IsPointNearLine(const mitk::Point2D &point,
const mitk::Point2D &startPoint,
const mitk::Point2D &endPoint,
mitk::Point2D &projectedPoint) const;
/** \brief Returns true if the point contained in the passed event (in display coordinates)
* is over the planar figure (with a pre-defined tolerance range); false otherwise. */
int IsPositionOverFigure(const InteractionPositionEvent *positionEvent,
PlanarFigure *planarFigure,
const PlaneGeometry *planarFigureGeometry,
const PlaneGeometry *rendererGeometry,
Point2D &pointProjectedOntoLine) const;
/** \brief Returns the index of the marker (control point) over which the point contained
* in the passed event (in display coordinates) currently is; -1 if the point is not over
* a marker. */
int IsPositionInsideMarker(const InteractionPositionEvent *positionEvent,
const PlanarFigure *planarFigure,
const PlaneGeometry *planarFigureGeometry,
const PlaneGeometry *rendererGeometry,
const BaseRenderer *renderer) const;
void LogPrintPlanarFigureQuantities(const PlanarFigure *planarFigure);
void ConfigurationChanged() override;
private:
/** \brief to store the value of precision to pick a point */
ScalarType m_Precision;
/** \brief Store the minimal distance between two control points. */
ScalarType m_MinimumPointDistance;
/** \brief True if the mouse is currently hovering over the image. */
bool m_IsHovering;
};
}
#endif // MITKPLANARFIGUREINTERACTOR_H
diff --git a/Modules/PlanarFigure/src/Interactions/mitkPlanarFigureInteractor.cpp b/Modules/PlanarFigure/src/Interactions/mitkPlanarFigureInteractor.cpp
index 706ac0b24b..9556791b3f 100644
--- a/Modules/PlanarFigure/src/Interactions/mitkPlanarFigureInteractor.cpp
+++ b/Modules/PlanarFigure/src/Interactions/mitkPlanarFigureInteractor.cpp
@@ -1,1115 +1,1129 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#define PLANARFIGUREINTERACTOR_DBG MITK_DEBUG("PlanarFigureInteractor") << __LINE__ << ": "
#include "mitkPlanarFigureInteractor.h"
#include "mitkPlanarBezierCurve.h"
#include "mitkPlanarCircle.h"
#include "mitkPlanarFigure.h"
#include "mitkPlanarPolygon.h"
#include "mitkInteractionPositionEvent.h"
#include "mitkInternalEvent.h"
#include "mitkBaseRenderer.h"
#include "mitkRenderingManager.h"
#include "mitkAbstractTransformGeometry.h"
#include "mitkPlaneGeometry.h"
+namespace mitk
+{
+ itkEventMacroDefinition(PlanarFigureEvent, itk::AnyEvent);
+ itkEventMacroDefinition(StartPlacementPlanarFigureEvent, PlanarFigureEvent);
+ itkEventMacroDefinition(EndPlacementPlanarFigureEvent, PlanarFigureEvent);
+ itkEventMacroDefinition(SelectPlanarFigureEvent, PlanarFigureEvent);
+ itkEventMacroDefinition(StartInteractionPlanarFigureEvent, PlanarFigureEvent);
+ itkEventMacroDefinition(EndInteractionPlanarFigureEvent, PlanarFigureEvent);
+ itkEventMacroDefinition(StartHoverPlanarFigureEvent, PlanarFigureEvent);
+ itkEventMacroDefinition(EndHoverPlanarFigureEvent, PlanarFigureEvent);
+ itkEventMacroDefinition(ContextMenuPlanarFigureEvent, PlanarFigureEvent);
+ itkEventMacroDefinition(PointMovedPlanarFigureEvent, PlanarFigureEvent);
+}
+
mitk::PlanarFigureInteractor::PlanarFigureInteractor()
: DataInteractor()
, m_Precision(6.5)
, m_MinimumPointDistance(25.0)
, m_IsHovering(false)
{
}
mitk::PlanarFigureInteractor::~PlanarFigureInteractor()
{
}
void mitk::PlanarFigureInteractor::ConnectActionsAndFunctions()
{
CONNECT_CONDITION("figure_is_on_current_slice", CheckFigureOnRenderingGeometry);
CONNECT_CONDITION("figure_is_placed", CheckFigurePlaced);
CONNECT_CONDITION("minimal_figure_is_finished", CheckMinimalFigureFinished);
CONNECT_CONDITION("hovering_above_figure", CheckFigureHovering);
CONNECT_CONDITION("hovering_above_point", CheckControlPointHovering);
CONNECT_CONDITION("figure_is_selected", CheckSelection);
CONNECT_CONDITION("point_is_valid", CheckPointValidity);
CONNECT_CONDITION("figure_is_finished", CheckFigureFinished);
CONNECT_CONDITION("reset_on_point_select_needed", CheckResetOnPointSelect);
CONNECT_CONDITION("points_can_be_added_or_removed", CheckFigureIsExtendable);
CONNECT_CONDITION("figure_can_be_deleted", CheckFigureIsDeletable);
CONNECT_CONDITION("figure_is_editable", CheckFigureIsEditable);
CONNECT_FUNCTION("finalize_figure", FinalizeFigure);
CONNECT_FUNCTION("hide_preview_point", HidePreviewPoint)
CONNECT_FUNCTION("hide_control_points", HideControlPoints)
CONNECT_FUNCTION("set_preview_point_position", SetPreviewPointPosition)
CONNECT_FUNCTION("move_current_point", MoveCurrentPoint);
CONNECT_FUNCTION("deselect_point", DeselectPoint);
CONNECT_FUNCTION("add_new_point", AddPoint);
CONNECT_FUNCTION("add_initial_point", AddInitialPoint);
CONNECT_FUNCTION("remove_selected_point", RemoveSelectedPoint);
CONNECT_FUNCTION("request_context_menu", RequestContextMenu);
CONNECT_FUNCTION("select_figure", SelectFigure);
CONNECT_FUNCTION("select_point", SelectPoint);
CONNECT_FUNCTION("end_interaction", EndInteraction);
CONNECT_FUNCTION("start_hovering", StartHovering)
CONNECT_FUNCTION("end_hovering", EndHovering);
CONNECT_FUNCTION("delete_figure", DeleteFigure);
CONNECT_FUNCTION("reset_on_point_select", PerformPointResetOnSelect);
}
bool mitk::PlanarFigureInteractor::CheckFigurePlaced(const InteractionEvent * /*interactionEvent*/)
{
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return false;
}
bool isFigureFinished = false;
planarFigure->GetPropertyList()->GetBoolProperty("initiallyplaced", isFigureFinished);
return planarFigure->IsPlaced() && isFigureFinished;
}
void mitk::PlanarFigureInteractor::MoveCurrentPoint(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return;
}
bool isEditable = true;
GetDataNode()->GetBoolProperty("planarfigure.iseditable", isEditable);
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return;
}
auto planarFigureGeometry = planarFigure->GetPlaneGeometry();
if (nullptr == planarFigureGeometry)
{
return;
}
auto abstractTransformGeometry = dynamic_cast<AbstractTransformGeometry*>(planarFigure->GetGeometry(0));
if (nullptr != abstractTransformGeometry)
{
return;
}
// Extract point in 2D world coordinates (relative to PlaneGeometry of PlanarFigure)
Point2D point2D;
if (!this->TransformPositionEventToPoint2D(positionEvent, planarFigureGeometry, point2D) || !isEditable)
{
return;
}
planarFigure->InvokeEvent(StartInteractionPlanarFigureEvent());
// check if the control points shall be hidden during interaction
bool hidecontrolpointsduringinteraction = false;
GetDataNode()->GetBoolProperty("planarfigure.hidecontrolpointsduringinteraction", hidecontrolpointsduringinteraction);
// hide the control points if necessary
// interactionEvent->GetSender()->GetDataStorage()->BlockNodeModifiedEvents( true );
GetDataNode()->SetBoolProperty("planarfigure.drawcontrolpoints", !hidecontrolpointsduringinteraction);
// interactionEvent->GetSender()->GetDataStorage()->BlockNodeModifiedEvents( false );
// Move current control point to this point
planarFigure->SetCurrentControlPoint(point2D);
// Re-evaluate features
planarFigure->EvaluateFeatures();
// Update rendered scene
RenderingManager::GetInstance()->RequestUpdateAll();
planarFigure->InvokeEvent(PointMovedPlanarFigureEvent());
}
void mitk::PlanarFigureInteractor::FinalizeFigure(StateMachineAction *, InteractionEvent *)
{
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return;
}
planarFigure->Modified();
planarFigure->DeselectControlPoint();
planarFigure->RemoveLastControlPoint();
planarFigure->SetProperty("initiallyplaced", mitk::BoolProperty::New(true));
GetDataNode()->SetBoolProperty("planarfigure.drawcontrolpoints", true);
GetDataNode()->Modified();
planarFigure->InvokeEvent(EndPlacementPlanarFigureEvent());
planarFigure->InvokeEvent(EndInteractionPlanarFigureEvent());
// Shape might change when figure is finalized, e.g., smoothing of subdivision polygon
planarFigure->EvaluateFeatures();
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::PlanarFigureInteractor::EndInteraction(StateMachineAction *, InteractionEvent *)
{
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return;
}
GetDataNode()->SetBoolProperty("planarfigure.drawcontrolpoints", true);
planarFigure->Modified();
planarFigure->InvokeEvent(EndInteractionPlanarFigureEvent());
RenderingManager::GetInstance()->RequestUpdateAll();
}
bool mitk::PlanarFigureInteractor::FilterEvents(InteractionEvent *interactionEvent, mitk::DataNode * /*dataNode*/)
{
if (interactionEvent->GetSender() == nullptr)
return false;
if (interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard3D)
return false;
return true;
}
void mitk::PlanarFigureInteractor::EndHovering(StateMachineAction *, InteractionEvent *)
{
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return;
}
planarFigure->ResetPreviewContolPoint();
// Invoke end-hover event once the mouse is exiting the figure area
m_IsHovering = false;
planarFigure->InvokeEvent(EndHoverPlanarFigureEvent());
// Set bool property to indicate that planar figure is no longer in "hovering" mode
GetDataNode()->SetBoolProperty("planarfigure.ishovering", false);
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::PlanarFigureInteractor::DeleteFigure(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return;
}
planarFigure->RemoveAllObservers();
GetDataNode()->RemoveAllObservers();
interactionEvent->GetSender()->GetDataStorage()->Remove(GetDataNode());
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::PlanarFigureInteractor::PerformPointResetOnSelect(StateMachineAction *, InteractionEvent *)
{
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return;
}
planarFigure->ResetOnPointSelect();
}
bool mitk::PlanarFigureInteractor::CheckMinimalFigureFinished(const InteractionEvent * /*interactionEvent*/)
{
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return false;
}
return planarFigure->GetNumberOfControlPoints() >= planarFigure->GetMinimumNumberOfControlPoints();
}
bool mitk::PlanarFigureInteractor::CheckFigureFinished(const InteractionEvent * /*interactionEvent*/)
{
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return false;
}
return planarFigure->GetNumberOfControlPoints() >= planarFigure->GetMaximumNumberOfControlPoints();
}
bool mitk::PlanarFigureInteractor::CheckFigureIsExtendable(const InteractionEvent * /*interactionEvent*/)
{
bool isExtendable(false);
GetDataNode()->GetBoolProperty("planarfigure.isextendable", isExtendable);
return isExtendable;
}
bool mitk::PlanarFigureInteractor::CheckFigureIsDeletable(const InteractionEvent * /*interactionEvent*/)
{
bool isDeletable(true);
GetDataNode()->GetBoolProperty("planarfigure.isdeletable", isDeletable);
return isDeletable;
}
bool mitk::PlanarFigureInteractor::CheckFigureIsEditable(const InteractionEvent * /*interactionEvent*/)
{
bool isEditable(true);
GetDataNode()->GetBoolProperty("planarfigure.iseditable", isEditable);
return isEditable;
}
void mitk::PlanarFigureInteractor::DeselectPoint(StateMachineAction *, InteractionEvent * /*interactionEvent*/)
{
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return;
}
const bool wasSelected = planarFigure->DeselectControlPoint();
if (wasSelected)
{
// Issue event so that listeners may update themselves
planarFigure->Modified();
planarFigure->InvokeEvent(EndInteractionPlanarFigureEvent());
GetDataNode()->SetBoolProperty("planarfigure.drawcontrolpoints", true);
GetDataNode()->Modified();
}
}
void mitk::PlanarFigureInteractor::AddPoint(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return;
}
/*
* Added check for "initiallyplaced" due to bug 13097:
*
* There are two possible cases in which a point can be inserted into a PlanarPolygon:
*
* 1. The figure is currently drawn -> the point will be appended at the end of the figure
* 2. A point is inserted at a userdefined position after the initial placement of the figure is finished
*
* In the second case we need to determine the proper insertion index. In the first case the index always has
* to be -1 so that the point is appended to the end.
*
* These changes are necessary because of a macOS specific issue: If a users draws a PlanarPolygon then the
* next point to be added moves according to the mouse position. If then the user left clicks in order to add
* a point one would assume the last move position is identical to the left click position. This is actually the
* case for windows and linux but somehow NOT for mac. Because of the insertion logic of a new point in the
* PlanarFigure then for mac the wrong current selected point is determined.
*
* With this check here this problem can be avoided. However a redesign of the insertion logic should be considered
*/
const DataNode::Pointer node = this->GetDataNode();
const BaseData::Pointer data = node->GetData();
bool isFigureFinished = false;
data->GetPropertyList()->GetBoolProperty("initiallyplaced", isFigureFinished);
bool selected = false;
bool isEditable = true;
node->GetBoolProperty("selected", selected);
node->GetBoolProperty("planarfigure.iseditable", isEditable);
if (!selected || !isEditable)
{
return;
}
auto planarFigure = dynamic_cast<PlanarFigure*>(data.GetPointer());
if (nullptr == planarFigure)
{
return;
}
// We can't derive a new control point from a polyline of a Bezier curve
// as all control points contribute to each polyline point.
if (dynamic_cast<PlanarBezierCurve *>(planarFigure) != nullptr && isFigureFinished)
return;
auto planarFigureGeometry = planarFigure->GetPlaneGeometry();
if (nullptr == planarFigureGeometry)
{
return;
}
auto abstractTransformGeometry = dynamic_cast<AbstractTransformGeometry*>(planarFigure->GetGeometry(0));
if (nullptr != abstractTransformGeometry)
{
return;
}
// If the planarFigure already has reached the maximum number
if (planarFigure->GetNumberOfControlPoints() >= planarFigure->GetMaximumNumberOfControlPoints())
{
return;
}
// Extract point in 2D world coordinates (relative to PlaneGeometry of
// PlanarFigure)
Point2D point2D, projectedPoint;
if (!this->TransformPositionEventToPoint2D(positionEvent, planarFigureGeometry, point2D))
{
return;
}
// TODO: check segment of polyline we clicked in
int nextIndex = -1;
// We only need to check which position to insert the control point
// when interacting with a PlanarPolygon. For all other types
// new control points will always be appended
const mitk::BaseRenderer *renderer = interactionEvent->GetSender();
const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldPlaneGeometry();
if (dynamic_cast<mitk::PlanarPolygon *>(planarFigure) && isFigureFinished)
{
nextIndex =
this->IsPositionOverFigure(positionEvent, planarFigure, planarFigureGeometry, projectionPlane, projectedPoint);
}
// Add point as new control point
if (planarFigure->IsPreviewControlPointVisible())
{
point2D = planarFigure->GetPreviewControlPoint();
}
planarFigure->AddControlPoint(point2D, planarFigure->GetControlPointForPolylinePoint(nextIndex, 0));
if (planarFigure->IsPreviewControlPointVisible())
{
planarFigure->SelectControlPoint(nextIndex);
planarFigure->ResetPreviewContolPoint();
}
// Re-evaluate features
planarFigure->EvaluateFeatures();
// this->LogPrintPlanarFigureQuantities( planarFigure );
// Update rendered scene
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::PlanarFigureInteractor::AddInitialPoint(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return;
}
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return;
}
mitk::BaseRenderer *renderer = interactionEvent->GetSender();
auto abstractTransformGeometry = dynamic_cast<AbstractTransformGeometry*>(planarFigure->GetGeometry(0));
// Invoke event to notify listeners that placement of this PF starts now
planarFigure->InvokeEvent(StartPlacementPlanarFigureEvent());
// Use PlaneGeometry of the renderer clicked on for this PlanarFigure
auto *planeGeometry = const_cast<mitk::PlaneGeometry *>(
dynamic_cast<const mitk::PlaneGeometry *>(renderer->GetSliceNavigationController()->GetCurrentPlaneGeometry()));
if (planeGeometry != nullptr && abstractTransformGeometry == nullptr)
{
planarFigure->SetPlaneGeometry(planeGeometry);
}
else
{
return;
}
// Extract point in 2D world coordinates (relative to PlaneGeometry of
// PlanarFigure)
Point2D point2D;
if (!this->TransformPositionEventToPoint2D(positionEvent, planeGeometry, point2D))
{
return;
}
// Place PlanarFigure at this point
planarFigure->PlaceFigure(point2D);
// Re-evaluate features
planarFigure->EvaluateFeatures();
// this->LogPrintPlanarFigureQuantities( planarFigure );
// Set a bool property indicating that the figure has been placed in
// the current RenderWindow. This is required so that the same render
// window can be re-aligned to the PlaneGeometry of the PlanarFigure later
// on in an application.
GetDataNode()->SetBoolProperty("PlanarFigureInitializedWindow", true, renderer);
// Update rendered scene
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::PlanarFigureInteractor::StartHovering(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return;
}
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return;
}
if (!m_IsHovering)
{
// Invoke hover event once when the mouse is entering the figure area
m_IsHovering = true;
planarFigure->InvokeEvent(StartHoverPlanarFigureEvent());
// Set bool property to indicate that planar figure is currently in "hovering" mode
GetDataNode()->SetBoolProperty("planarfigure.ishovering", true);
RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void mitk::PlanarFigureInteractor::SetPreviewPointPosition(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return;
}
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return;
}
const mitk::BaseRenderer *renderer = interactionEvent->GetSender();
planarFigure->DeselectControlPoint();
mitk::Point2D pointProjectedOntoLine = positionEvent->GetPointerPositionOnScreen();
bool selected(false);
bool isExtendable(false);
bool isEditable(true);
GetDataNode()->GetBoolProperty("selected", selected);
GetDataNode()->GetBoolProperty("planarfigure.isextendable", isExtendable);
GetDataNode()->GetBoolProperty("planarfigure.iseditable", isEditable);
if (selected && isExtendable && isEditable)
{
renderer->DisplayToPlane(pointProjectedOntoLine, pointProjectedOntoLine);
planarFigure->SetPreviewControlPoint(pointProjectedOntoLine);
}
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::PlanarFigureInteractor::HideControlPoints(StateMachineAction *, InteractionEvent * /*interactionEvent*/)
{
GetDataNode()->SetBoolProperty("planarfigure.drawcontrolpoints", false);
}
void mitk::PlanarFigureInteractor::HidePreviewPoint(StateMachineAction *, InteractionEvent *)
{
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return;
}
planarFigure->ResetPreviewContolPoint();
RenderingManager::GetInstance()->RequestUpdateAll();
}
bool mitk::PlanarFigureInteractor::CheckFigureHovering(const InteractionEvent *interactionEvent)
{
auto positionEvent = dynamic_cast<const InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return false;
}
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return false;
}
auto planarFigureGeometry = planarFigure->GetPlaneGeometry();
if (nullptr == planarFigureGeometry)
{
return false;
}
auto abstractTransformGeometry = dynamic_cast<AbstractTransformGeometry*>(planarFigure->GetGeometry(0));
if (nullptr != abstractTransformGeometry)
{
return false;
}
const mitk::BaseRenderer *renderer = interactionEvent->GetSender();
const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldPlaneGeometry();
mitk::Point2D pointProjectedOntoLine;
int previousControlPoint = this->IsPositionOverFigure(
positionEvent, planarFigure, planarFigureGeometry, projectionPlane, pointProjectedOntoLine);
bool isHovering = (previousControlPoint != -1);
return isHovering;
}
bool mitk::PlanarFigureInteractor::CheckControlPointHovering(const InteractionEvent *interactionEvent)
{
auto positionEvent = dynamic_cast<const InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return false;
}
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return false;
}
auto planarFigureGeometry = planarFigure->GetPlaneGeometry();
if (nullptr == planarFigureGeometry)
{
return false;
}
auto abstractTransformGeometry = dynamic_cast<AbstractTransformGeometry*>(planarFigure->GetGeometry(0));
if (nullptr != abstractTransformGeometry)
{
return false;
}
const mitk::BaseRenderer *renderer = interactionEvent->GetSender();
const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldPlaneGeometry();
int pointIndex = -1;
pointIndex = mitk::PlanarFigureInteractor::IsPositionInsideMarker(
positionEvent, planarFigure, planarFigureGeometry, projectionPlane, renderer);
return pointIndex >= 0;
}
bool mitk::PlanarFigureInteractor::CheckSelection(const InteractionEvent * /*interactionEvent*/)
{
bool selected = false;
GetDataNode()->GetBoolProperty("selected", selected);
return selected;
}
void mitk::PlanarFigureInteractor::SelectFigure(StateMachineAction *, InteractionEvent * /*interactionEvent*/)
{
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return;
}
planarFigure->InvokeEvent(SelectPlanarFigureEvent());
}
void mitk::PlanarFigureInteractor::SelectPoint(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return;
}
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return;
}
auto planarFigureGeometry = planarFigure->GetPlaneGeometry();
if (nullptr == planarFigureGeometry)
{
return;
}
auto abstractTransformGeometry = dynamic_cast<AbstractTransformGeometry*>(planarFigure->GetGeometry(0));
if (nullptr != abstractTransformGeometry)
{
return;
}
const mitk::BaseRenderer *renderer = interactionEvent->GetSender();
const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldPlaneGeometry();
const int pointIndex = mitk::PlanarFigureInteractor::IsPositionInsideMarker(
positionEvent, planarFigure, planarFigureGeometry, projectionPlane, renderer);
if (pointIndex >= 0)
{
// If mouse is above control point, mark it as selected
planarFigure->SelectControlPoint(pointIndex);
}
else
{
planarFigure->DeselectControlPoint();
}
}
bool mitk::PlanarFigureInteractor::CheckPointValidity(const InteractionEvent *interactionEvent)
{
// Check if the distance of the current point to the previously set point in display coordinates
// is sufficient (if a previous point exists)
auto positionEvent = dynamic_cast<const InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return false;
}
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return false;
}
return IsMousePositionAcceptableAsNewControlPoint(positionEvent, planarFigure);
}
void mitk::PlanarFigureInteractor::RemoveSelectedPoint(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return;
}
const int selectedControlPoint = planarFigure->GetSelectedControlPoint();
planarFigure->RemoveControlPoint(selectedControlPoint);
// Re-evaluate features
planarFigure->EvaluateFeatures();
planarFigure->Modified();
GetDataNode()->SetBoolProperty("planarfigure.drawcontrolpoints", true);
planarFigure->InvokeEvent(EndInteractionPlanarFigureEvent());
RenderingManager::GetInstance()->RequestUpdateAll();
mitk::BaseRenderer *renderer = interactionEvent->GetSender();
HandleEvent(mitk::InternalEvent::New(renderer, this, "Dummy-Event"), GetDataNode());
}
void mitk::PlanarFigureInteractor::RequestContextMenu(StateMachineAction *, InteractionEvent * /*interactionEvent*/)
{
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return;
}
bool selected = false;
GetDataNode()->GetBoolProperty("selected", selected);
// no need to invoke this if the figure is already selected
if (!selected)
{
planarFigure->InvokeEvent(SelectPlanarFigureEvent());
}
planarFigure->InvokeEvent(ContextMenuPlanarFigureEvent());
}
bool mitk::PlanarFigureInteractor::CheckResetOnPointSelect(const InteractionEvent * /*interactionEvent*/)
{
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return false;
}
bool isEditable = true;
GetDataNode()->GetBoolProperty("planarfigure.iseditable", isEditable);
// Reset the PlanarFigure if required
return isEditable && planarFigure->ResetOnPointSelectNeeded();
}
bool mitk::PlanarFigureInteractor::CheckFigureOnRenderingGeometry(const InteractionEvent *interactionEvent)
{
auto positionEvent = dynamic_cast<const InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
{
return false;
}
const mitk::Point3D worldPoint3D = positionEvent->GetPositionInWorld();
auto planarFigure = dynamic_cast<PlanarFigure*>(GetDataNode()->GetData());
if (nullptr == planarFigure)
{
return false;
}
auto planarFigureGeometry = planarFigure->GetPlaneGeometry();
if (nullptr == planarFigureGeometry)
{
return false;
}
auto abstractTransformGeometry = dynamic_cast<AbstractTransformGeometry*>(planarFigure->GetGeometry(0));
if (nullptr != abstractTransformGeometry)
{
return false;
}
const double planeThickness = planarFigureGeometry->GetExtentInMM(2);
return planarFigureGeometry->Distance(worldPoint3D) <= planeThickness;
}
void mitk::PlanarFigureInteractor::SetPrecision(mitk::ScalarType precision)
{
m_Precision = precision;
}
void mitk::PlanarFigureInteractor::SetMinimumPointDistance(ScalarType minimumDistance)
{
m_MinimumPointDistance = minimumDistance;
}
bool mitk::PlanarFigureInteractor::TransformPositionEventToPoint2D(const InteractionPositionEvent *positionEvent,
const PlaneGeometry *planarFigureGeometry,
Point2D &point2D)
{
if (nullptr == positionEvent || nullptr == planarFigureGeometry)
{
return false;
}
const mitk::Point3D worldPoint3D = positionEvent->GetPositionInWorld();
// TODO: proper handling of distance tolerance
if (planarFigureGeometry->Distance(worldPoint3D) > 0.1)
{
return false;
}
// Project point onto plane of this PlanarFigure
planarFigureGeometry->Map(worldPoint3D, point2D);
return true;
}
bool mitk::PlanarFigureInteractor::TransformObjectToDisplay(const mitk::Point2D &point2D,
mitk::Point2D &displayPoint,
const mitk::PlaneGeometry *objectGeometry,
const mitk::PlaneGeometry *rendererGeometry,
const mitk::BaseRenderer *renderer) const
{
if (nullptr == objectGeometry || nullptr == rendererGeometry || nullptr == renderer)
{
return false;
}
mitk::Point3D point3D;
// Map circle point from local 2D geometry into 3D world space
objectGeometry->Map(point2D, point3D);
const double planeThickness = objectGeometry->GetExtentInMM(2);
// TODO: proper handling of distance tolerance
if (rendererGeometry->Distance(point3D) < planeThickness / 3.0)
{
// Project 3D world point onto display geometry
renderer->WorldToDisplay(point3D, displayPoint);
return true;
}
return false;
}
bool mitk::PlanarFigureInteractor::IsPointNearLine(const mitk::Point2D &point,
const mitk::Point2D &startPoint,
const mitk::Point2D &endPoint,
mitk::Point2D &projectedPoint) const
{
mitk::Vector2D n1 = endPoint - startPoint;
n1.Normalize();
// Determine dot products between line vector and startpoint-point / endpoint-point vectors
const double l1 = n1 * (point - startPoint);
const double l2 = -n1 * (point - endPoint);
// Determine projection of specified point onto line defined by start / end point
const mitk::Point2D crossPoint = startPoint + n1 * l1;
projectedPoint = crossPoint;
const float dist1 = crossPoint.SquaredEuclideanDistanceTo(point);
const float dist2 = endPoint.SquaredEuclideanDistanceTo(point);
const float dist3 = startPoint.SquaredEuclideanDistanceTo(point);
// Point is inside encompassing rectangle IF
// - its distance to its projected point is small enough
// - it is not further outside of the line than the defined tolerance
if (((dist1 < 20.0) && (l1 > 0.0) && (l2 > 0.0)) || dist2 < 20.0 || dist3 < 20.0)
{
return true;
}
return false;
}
int mitk::PlanarFigureInteractor::IsPositionOverFigure(const InteractionPositionEvent *positionEvent,
PlanarFigure *planarFigure,
const PlaneGeometry *planarFigureGeometry,
const PlaneGeometry *rendererGeometry,
Point2D &pointProjectedOntoLine) const
{
if (nullptr == positionEvent || nullptr == planarFigure || nullptr == planarFigureGeometry
|| nullptr == rendererGeometry)
{
return -1;
}
mitk::Point2D displayPosition = positionEvent->GetPointerPositionOnScreen();
// Iterate over all polylines of planar figure, and check if
// any one is close to the current display position
typedef mitk::PlanarFigure::PolyLineType VertexContainerType;
Point2D polyLinePoint;
Point2D firstPolyLinePoint;
Point2D previousPolyLinePoint;
for (unsigned short loop = 0; loop < planarFigure->GetPolyLinesSize(); ++loop)
{
const VertexContainerType polyLine = planarFigure->GetPolyLine(loop);
bool firstPoint(true);
for (auto it = polyLine.begin(); it != polyLine.end(); ++it)
{
// Get plane coordinates of this point of polyline (if possible)
if (!this->TransformObjectToDisplay(
*it, polyLinePoint, planarFigureGeometry, rendererGeometry, positionEvent->GetSender()))
{
break; // Poly line invalid (not on current 2D plane) --> skip it
}
if (firstPoint)
{
firstPolyLinePoint = polyLinePoint;
firstPoint = false;
}
else if (this->IsPointNearLine(displayPosition, previousPolyLinePoint, polyLinePoint, pointProjectedOntoLine))
{
// Point is close enough to line segment --> Return index of the segment
return std::distance(polyLine.begin(), it);
}
previousPolyLinePoint = polyLinePoint;
}
// For closed figures, also check last line segment
if (planarFigure->IsClosed() &&
this->IsPointNearLine(displayPosition, polyLinePoint, firstPolyLinePoint, pointProjectedOntoLine))
{
return 0; // Return index of first control point
}
}
return -1;
}
int mitk::PlanarFigureInteractor::IsPositionInsideMarker(const InteractionPositionEvent *positionEvent,
const PlanarFigure *planarFigure,
const PlaneGeometry *planarFigureGeometry,
const PlaneGeometry *rendererGeometry,
const BaseRenderer *renderer) const
{
if (nullptr == positionEvent || nullptr == planarFigure || nullptr == planarFigureGeometry
|| nullptr == rendererGeometry || nullptr == renderer)
{
return -1;
}
const mitk::Point2D displayPosition = positionEvent->GetPointerPositionOnScreen();
// Iterate over all control points of planar figure, and check if
// any one is close to the current display position
mitk::Point2D displayControlPoint;
const int numberOfControlPoints = planarFigure->GetNumberOfControlPoints();
for (int i = 0; i < numberOfControlPoints; i++)
{
if (this->TransformObjectToDisplay(
planarFigure->GetControlPoint(i), displayControlPoint, planarFigureGeometry, rendererGeometry, renderer))
{
// TODO: variable size of markers
if (displayPosition.SquaredEuclideanDistanceTo(displayControlPoint) < 20.0)
{
return i;
}
}
}
return -1;
}
void mitk::PlanarFigureInteractor::LogPrintPlanarFigureQuantities(const PlanarFigure *planarFigure)
{
if (nullptr == planarFigure)
{
MITK_INFO << "PlanarFigure invalid.";
}
MITK_INFO << "PlanarFigure: " << planarFigure->GetNameOfClass();
for (unsigned int i = 0; i < planarFigure->GetNumberOfFeatures(); ++i)
{
MITK_INFO << "* " << planarFigure->GetFeatureName(i) << ": " << planarFigure->GetQuantity(i) << " "
<< planarFigure->GetFeatureUnit(i);
}
}
bool mitk::PlanarFigureInteractor::IsMousePositionAcceptableAsNewControlPoint(
const mitk::InteractionPositionEvent *positionEvent, const PlanarFigure *planarFigure)
{
if (nullptr == positionEvent || nullptr == planarFigure)
{
return false;
}
const BaseRenderer *renderer = positionEvent->GetSender();
if (nullptr == renderer)
{
return false;
}
// Get the timestep to support 3D+t
const int timeStep(renderer->GetTimeStep(planarFigure));
bool tooClose(false);
auto planarFigureGeometry = dynamic_cast<mitk::PlaneGeometry*>(planarFigure->GetGeometry(timeStep));
if (nullptr == planarFigureGeometry)
{
return false;
}
auto abstractTransformGeometry = dynamic_cast<AbstractTransformGeometry*>(planarFigure->GetGeometry(timeStep));
if (nullptr != abstractTransformGeometry)
{
return false;
}
Point2D point2D;
// Get the point2D from the positionEvent
if (!this->TransformPositionEventToPoint2D(positionEvent, planarFigureGeometry, point2D))
{
return false;
}
// apply the controlPoint constraints of the planarFigure to get the
// coordinates that would actually be used.
const Point2D correctedPoint = const_cast<PlanarFigure *>(planarFigure)->ApplyControlPointConstraints(0, point2D);
// map the 2D coordinates of the new point to world-coordinates
// and transform those to display-coordinates
mitk::Point3D newPoint3D;
planarFigureGeometry->Map(correctedPoint, newPoint3D);
mitk::Point2D newDisplayPosition;
renderer->WorldToDisplay(newPoint3D, newDisplayPosition);
const int selectedControlPoint = planarFigure->GetSelectedControlPoint();
for (int i = 0; i < (int)planarFigure->GetNumberOfControlPoints(); ++i)
{
if (i != selectedControlPoint)
{
// Try to convert previous point to current display coordinates
mitk::Point3D previousPoint3D;
// map the 2D coordinates of the control-point to world-coordinates
planarFigureGeometry->Map(planarFigure->GetControlPoint(i), previousPoint3D);
if (renderer->GetCurrentWorldPlaneGeometry()->Distance(previousPoint3D) < 0.1) // ugly, but assert makes this work
{
mitk::Point2D previousDisplayPosition;
// transform the world-coordinates into display-coordinates
renderer->WorldToDisplay(previousPoint3D, previousDisplayPosition);
// Calculate the distance. We use display-coordinates here to make
// the check independent of the zoom-level of the rendering scene.
const double a = newDisplayPosition[0] - previousDisplayPosition[0];
const double b = newDisplayPosition[1] - previousDisplayPosition[1];
// If point is to close, do not set a new point
tooClose = (a * a + b * b < m_MinimumPointDistance);
}
if (tooClose)
return false; // abort loop early
}
}
return !tooClose; // default
}
void mitk::PlanarFigureInteractor::ConfigurationChanged()
{
const mitk::PropertyList::Pointer properties = GetAttributes();
std::string precision = "";
if (properties->GetStringProperty("precision", precision))
{
m_Precision = atof(precision.c_str());
}
else
{
m_Precision = (ScalarType)6.5;
}
std::string minPointDistance = "";
if (properties->GetStringProperty("minPointDistance", minPointDistance))
{
m_MinimumPointDistance = atof(minPointDistance.c_str());
}
else
{
m_MinimumPointDistance = (ScalarType)25.0;
}
}
diff --git a/Modules/Python/autoload/PythonService/mitkPythonService.cpp b/Modules/Python/autoload/PythonService/mitkPythonService.cpp
index 4dd7ae1c18..513a7fe647 100644
--- a/Modules/Python/autoload/PythonService/mitkPythonService.cpp
+++ b/Modules/Python/autoload/PythonService/mitkPythonService.cpp
@@ -1,917 +1,917 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkPythonService.h"
#include <Python.h>
#include <mitkIOUtil.h>
#include <QFile>
#include <QDir>
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 5208)
#endif
#include <PythonQt.h>
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#include "PythonPath.h"
#include <vtkPolyData.h>
#include <mitkRenderingManager.h>
#include <mitkImageReadAccessor.h>
#include <mitkImageWriteAccessor.h>
#include <QFileInfo>
#include <QCoreApplication>
#include <itksys/SystemTools.hxx>
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <numpy/arrayobject.h>
#include <mitkExceptionMacro.h>
#ifndef WIN32
#include <dlfcn.h>
#endif
typedef itksys::SystemTools ist;
mitk::PythonService::PythonService()
: m_ItkWrappingAvailable( true )
, m_OpenCVWrappingAvailable( true )
, m_VtkWrappingAvailable( true )
, m_ErrorOccured( false )
{
bool pythonInitialized = static_cast<bool>( Py_IsInitialized() ); //m_PythonManager.isPythonInitialized() );
// due to strange static var behaviour on windows Py_IsInitialized() returns correct value while
// m_PythonManager.isPythonInitialized() does not because it has been constructed and destructed again
if( !pythonInitialized )
{
MITK_INFO << "Initializing python service";
//TODO a better way to do this
#ifndef WIN32
dlerror();
if(dlopen(PYTHON_LIBRARY, RTLD_NOW | RTLD_GLOBAL) == nullptr )
{
mitkThrow() << "Python runtime could not be loaded: " << dlerror();
}
#endif
std::string programPath = QCoreApplication::applicationDirPath().toStdString() + "/";
QString pythonCommand;
pythonCommand.append( QString("import site, sys\n") );
pythonCommand.append( QString("import SimpleITK as sitk\n") );
pythonCommand.append( QString("import SimpleITK._SimpleITK as _SimpleITK\n") );
pythonCommand.append( QString("import numpy\n") );
pythonCommand.append( QString("sys.path.append('')\n") );
pythonCommand.append( QString("sys.path.append('%1')\n").arg(programPath.c_str()) );
pythonCommand.append( QString("sys.path.append('%1')\n").arg(EXTERNAL_DIST_PACKAGES) );
pythonCommand.append( QString("\nsite.addsitedir('%1')").arg(EXTERNAL_SITE_PACKAGES) );
if( pythonInitialized )
m_PythonManager.setInitializationFlags(PythonQt::RedirectStdOut|PythonQt::PythonAlreadyInitialized);
else
m_PythonManager.setInitializationFlags(PythonQt::RedirectStdOut);
m_PythonManager.initialize();
m_PythonManager.executeString( pythonCommand, ctkAbstractPythonManager::FileInput );
}
}
mitk::PythonService::~PythonService()
{
MITK_DEBUG("mitk::PythonService") << "destructing PythonService";
}
void mitk::PythonService::AddRelativeSearchDirs(std::vector< std::string > dirs)
{
std::string programPath = QCoreApplication::applicationDirPath().toStdString() + "/";
std::string cwd = ist::GetCurrentWorkingDirectory() + "/";
for (auto dir : dirs)
{
m_PythonManager.executeString(QString("sys.path.append('%1')").arg((programPath + dir).c_str()), ctkAbstractPythonManager::SingleInput );
m_PythonManager.executeString(QString("sys.path.append('%1')").arg((cwd + dir).c_str()), ctkAbstractPythonManager::SingleInput );
}
}
void mitk::PythonService::AddAbsoluteSearchDirs(std::vector< std::string > dirs)
{
for (auto dir : dirs)
{
m_PythonManager.executeString(QString("sys.path.append('%1')").arg(dir.c_str()), ctkAbstractPythonManager::SingleInput );
}
}
std::string mitk::PythonService::Execute(const std::string &stdpythonCommand, int commandType)
{
QString pythonCommand = QString::fromStdString(stdpythonCommand);
QVariant result;
bool commandIssued = true;
if(commandType == IPythonService::SINGLE_LINE_COMMAND )
result = m_PythonManager.executeString(pythonCommand, ctkAbstractPythonManager::SingleInput );
else if(commandType == IPythonService::MULTI_LINE_COMMAND )
result = m_PythonManager.executeString(pythonCommand, ctkAbstractPythonManager::FileInput );
else if(commandType == IPythonService::EVAL_COMMAND )
result = m_PythonManager.executeString(pythonCommand, ctkAbstractPythonManager::EvalInput );
else
commandIssued = false;
if(commandIssued)
{
this->NotifyObserver(pythonCommand.toStdString());
m_ErrorOccured = PythonQt::self()->hadError();
}
return result.toString().toStdString();
}
void mitk::PythonService::ExecuteScript( const std::string& pythonScript )
{
std::ifstream t(pythonScript.c_str());
std::string str((std::istreambuf_iterator<char>(t)),
std::istreambuf_iterator<char>());
t.close();
m_PythonManager.executeString(QString::fromStdString(str));
}
std::vector<mitk::PythonVariable> mitk::PythonService::GetVariableStack() const
{
std::vector<mitk::PythonVariable> list;
PyObject* dict = PyImport_GetModuleDict();
PyObject* object = PyDict_GetItemString(dict, "__main__");
PyObject* dirMain = PyObject_Dir(object);
PyObject* tempObject = nullptr;
//PyObject* strTempObject = 0;
if(dirMain)
{
std::string name, attrValue, attrType;
for(int i = 0; i<PyList_Size(dirMain); i++)
{
tempObject = PyList_GetItem(dirMain, i);
name = PyString_AsString(tempObject);
tempObject = PyObject_GetAttrString( object, name.c_str() );
attrType = tempObject->ob_type->tp_name;
if(tempObject && ( PyUnicode_Check(tempObject) || PyString_Check(tempObject) ) )
attrValue = PyString_AsString(tempObject);
else
attrValue = "";
mitk::PythonVariable var;
var.m_Name = name;
var.m_Value = attrValue;
var.m_Type = attrType;
list.push_back(var);
}
}
return list;
}
std::string mitk::PythonService::GetVariable(const std::string& name) const
{
std::vector<mitk::PythonVariable> allVars = this->GetVariableStack();
for(unsigned int i = 0; i< allVars.size(); i++)
{
if( allVars.at(i).m_Name == name )
return allVars.at(i).m_Value;
}
return "";
}
bool mitk::PythonService::DoesVariableExist(const std::string& name) const
{
bool varExists = false;
std::vector<mitk::PythonVariable> allVars = this->GetVariableStack();
for(unsigned int i = 0; i< allVars.size(); i++)
{
if( allVars.at(i).m_Name == name )
{
varExists = true;
break;
}
}
return varExists;
}
void mitk::PythonService::AddPythonCommandObserver(mitk::PythonCommandObserver *observer)
{
if(!m_Observer.contains(observer))
m_Observer.append(observer);
}
void mitk::PythonService::RemovePythonCommandObserver(mitk::PythonCommandObserver *observer)
{
m_Observer.removeOne(observer);
}
void mitk::PythonService::NotifyObserver(const std::string &command)
{
MITK_DEBUG("mitk::PythonService") << "number of observer " << m_Observer.size();
for( int i=0; i< m_Observer.size(); ++i )
{
m_Observer.at(i)->CommandExecuted(command);
}
}
bool mitk::PythonService::CopyToPythonAsSimpleItkImage(mitk::Image *image, const std::string &stdvarName)
{
QString varName = QString::fromStdString( stdvarName );
QString command;
unsigned int* imgDim = image->GetDimensions();
int npy_nd = 1;
// access python module
PyObject *pyMod = PyImport_AddModule("__main__");
// global dictionary
PyObject *pyDict = PyModule_GetDict(pyMod);
const mitk::Vector3D spacing = image->GetGeometry()->GetSpacing();
const mitk::Point3D origin = image->GetGeometry()->GetOrigin();
mitk::PixelType pixelType = image->GetPixelType();
- itk::ImageIOBase::IOPixelType ioPixelType = image->GetPixelType().GetPixelType();
+ auto ioPixelType = image->GetPixelType().GetPixelType();
PyObject* npyArray = nullptr;
mitk::ImageReadAccessor racc(image);
void* array = const_cast<void*>(racc.GetData());
mitk::Vector3D xDirection;
mitk::Vector3D yDirection;
mitk::Vector3D zDirection;
const vnl_matrix_fixed<ScalarType, 3, 3> &transform =
image->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix();
mitk::Vector3D s = image->GetGeometry()->GetSpacing();
// ToDo: Check if this is a collumn or row vector from the matrix.
// right now it works but not sure for rotated geometries
mitk::FillVector3D(xDirection, transform[0][0]/s[0], transform[0][1]/s[1], transform[0][2]/s[2]);
mitk::FillVector3D(yDirection, transform[1][0]/s[0], transform[1][1]/s[1], transform[1][2]/s[2]);
mitk::FillVector3D(zDirection, transform[2][0]/s[0], transform[2][1]/s[1], transform[2][2]/s[2]);
// save the total number of elements here (since the numpy array is one dimensional)
npy_intp* npy_dims = new npy_intp[1];
npy_dims[0] = imgDim[0];
/**
* Build a string in the format [1024,1028,1]
* to describe the dimensionality. This is needed for simple itk
* to know the dimensions of the image
*/
QString dimensionString;
dimensionString.append(QString("["));
dimensionString.append(QString::number(imgDim[0]));
for (unsigned i = 1; i < 3; ++i)
// always three because otherwise the 3d-geometry gets destroyed
// (relevant for backtransformation of simple itk image to mitk.
{
dimensionString.append(QString(","));
dimensionString.append(QString::number(imgDim[i]));
npy_dims[0] *= imgDim[i];
}
dimensionString.append("]");
// the next line is necessary for vectorimages
npy_dims[0] *= pixelType.GetNumberOfComponents();
// default pixeltype: unsigned short
NPY_TYPES npy_type = NPY_USHORT;
std::string sitk_type = "sitkUInt8";
- if( ioPixelType == itk::ImageIOBase::SCALAR )
+ if( ioPixelType == itk::IOPixelEnum::SCALAR )
{
- if( pixelType.GetComponentType() == itk::ImageIOBase::DOUBLE ) {
+ if( pixelType.GetComponentType() == itk::IOComponentEnum::DOUBLE ) {
npy_type = NPY_DOUBLE;
sitk_type = "sitkFloat64";
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::FLOAT ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::FLOAT ) {
npy_type = NPY_FLOAT;
sitk_type = "sitkFloat32";
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::SHORT) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::SHORT) {
npy_type = NPY_SHORT;
sitk_type = "sitkInt16";
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::CHAR ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::CHAR ) {
npy_type = NPY_BYTE;
sitk_type = "sitkInt8";
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::INT ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::INT ) {
npy_type = NPY_INT;
sitk_type = "sitkInt32";
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::LONG ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::LONG ) {
npy_type = NPY_LONG;
sitk_type = "sitkInt64";
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::UCHAR ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::UCHAR ) {
npy_type = NPY_UBYTE;
sitk_type = "sitkUInt8";
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::UINT ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::UINT ) {
npy_type = NPY_UINT;
sitk_type = "sitkUInt32";
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::ULONG ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::ULONG ) {
npy_type = NPY_LONG;
sitk_type = "sitkUInt64";
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::USHORT ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::USHORT ) {
npy_type = NPY_USHORT;
sitk_type = "sitkUInt16";
}
}
- else if ( ioPixelType == itk::ImageIOBase::VECTOR ||
- ioPixelType == itk::ImageIOBase::RGB ||
- ioPixelType == itk::ImageIOBase::RGBA
+ else if ( ioPixelType == itk::IOPixelEnum::VECTOR ||
+ ioPixelType == itk::IOPixelEnum::RGB ||
+ ioPixelType == itk::IOPixelEnum::RGBA
)
{
- if( pixelType.GetComponentType() == itk::ImageIOBase::DOUBLE ) {
+ if( pixelType.GetComponentType() == itk::IOComponentEnum::DOUBLE ) {
npy_type = NPY_DOUBLE;
sitk_type = "sitkVectorFloat64";
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::FLOAT ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::FLOAT ) {
npy_type = NPY_FLOAT;
sitk_type = "sitkVectorFloat32";
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::SHORT) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::SHORT) {
npy_type = NPY_SHORT;
sitk_type = "sitkVectorInt16";
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::CHAR ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::CHAR ) {
npy_type = NPY_BYTE;
sitk_type = "sitkVectorInt8";
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::INT ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::INT ) {
npy_type = NPY_INT;
sitk_type = "sitkVectorInt32";
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::LONG ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::LONG ) {
npy_type = NPY_LONG;
sitk_type = "sitkVectorInt64";
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::UCHAR ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::UCHAR ) {
npy_type = NPY_UBYTE;
sitk_type = "sitkVectorUInt8";
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::UINT ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::UINT ) {
npy_type = NPY_UINT;
sitk_type = "sitkVectorUInt32";
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::ULONG ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::ULONG ) {
npy_type = NPY_LONG;
sitk_type = "sitkVectorUInt64";
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::USHORT ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::USHORT ) {
npy_type = NPY_USHORT;
sitk_type = "sitkVectorUInt16";
}
}
else {
MITK_WARN << "not a recognized pixeltype";
return false;
}
// creating numpy array
import_array1 (true);
npyArray = PyArray_SimpleNewFromData(npy_nd,npy_dims,npy_type,array);
// add temp array it to the python dictionary to access it in python code
const int status = PyDict_SetItemString( pyDict,QString("%1_numpy_array")
.arg(varName).toStdString().c_str(),
npyArray );
// sanity check
if ( status != 0 )
return false;
command.append( QString("%1 = sitk.Image(%2,sitk.%3,%4)\n").arg(varName)
.arg(dimensionString)
.arg(QString(sitk_type.c_str())).arg(QString::number(pixelType.GetNumberOfComponents())) );
command.append( QString("%1.SetSpacing([%2,%3,%4])\n").arg(varName)
.arg(QString::number(spacing[0]))
.arg(QString::number(spacing[1]))
.arg(QString::number(spacing[2])) );
command.append( QString("%1.SetOrigin([%2,%3,%4])\n").arg(varName)
.arg(QString::number(origin[0]))
.arg(QString::number(origin[1]))
.arg(QString::number(origin[2])) );
command.append( QString("%1.SetDirection([%2,%3,%4,%5,%6,%7,%8,%9,%10])\n").arg(varName)
.arg(QString::number(xDirection[0]))
.arg(QString::number(xDirection[1]))
.arg(QString::number(xDirection[2]))
.arg(QString::number(yDirection[0]))
.arg(QString::number(yDirection[1]))
.arg(QString::number(yDirection[2]))
.arg(QString::number(zDirection[0]))
.arg(QString::number(zDirection[1]))
.arg(QString::number(zDirection[2]))
);
// directly access the cpp api from the lib
command.append( QString("_SimpleITK._SetImageFromArray(%1_numpy_array,%1)\n").arg(varName) );
command.append( QString("del %1_numpy_array").arg(varName) );
MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString();
this->Execute( command.toStdString(), IPythonService::MULTI_LINE_COMMAND );
return true;
}
mitk::PixelType DeterminePixelType(const std::string& pythonPixeltype, unsigned long nrComponents, int dimensions)
{
typedef itk::RGBPixel< unsigned char > UCRGBPixelType;
typedef itk::RGBPixel< unsigned short > USRGBPixelType;
typedef itk::RGBPixel< float > FloatRGBPixelType;
typedef itk::RGBPixel< double > DoubleRGBPixelType;
typedef itk::Image< UCRGBPixelType > UCRGBImageType;
typedef itk::Image< USRGBPixelType > USRGBImageType;
typedef itk::Image< FloatRGBPixelType > FloatRGBImageType;
typedef itk::Image< DoubleRGBPixelType > DoubleRGBImageType;
typedef itk::RGBAPixel< unsigned char > UCRGBAPixelType;
typedef itk::RGBAPixel< unsigned short > USRGBAPixelType;
typedef itk::RGBAPixel< float > FloatRGBAPixelType;
typedef itk::RGBAPixel< double > DoubleRGBAPixelType;
typedef itk::Image< UCRGBAPixelType > UCRGBAImageType;
typedef itk::Image< USRGBAPixelType > USRGBAImageType;
typedef itk::Image< FloatRGBAPixelType > FloatRGBAImageType;
typedef itk::Image< DoubleRGBAPixelType > DoubleRGBAImageType;
auto pixelType = mitk::MakePixelType<char, char >(nrComponents);
if (nrComponents == 1)
{
if( pythonPixeltype.compare("float64") == 0 ) {
pixelType = mitk::MakePixelType<double, double >(nrComponents);
} else if( pythonPixeltype.compare("float32") == 0 ) {
pixelType = mitk::MakePixelType<float, float >(nrComponents);
} else if( pythonPixeltype.compare("int16") == 0) {
pixelType = mitk::MakePixelType<short, short >(nrComponents);
} else if( pythonPixeltype.compare("int8") == 0 ) {
pixelType = mitk::MakePixelType<char, char >(nrComponents);
} else if( pythonPixeltype.compare("int32") == 0 ) {
pixelType = mitk::MakePixelType<int, int >(nrComponents);
} else if( pythonPixeltype.compare("int64") == 0 ) {
pixelType = mitk::MakePixelType<long, long >(nrComponents);
} else if( pythonPixeltype.compare("uint8") == 0 ) {
pixelType = mitk::MakePixelType<unsigned char, unsigned char >(nrComponents);
} else if( pythonPixeltype.compare("uint32") == 0 ) {
pixelType = mitk::MakePixelType<unsigned int, unsigned int >(nrComponents);
} else if( pythonPixeltype.compare("uint64") == 0 ) {
pixelType = mitk::MakePixelType<unsigned long, unsigned long >(nrComponents);
} else if( pythonPixeltype.compare("uint16") == 0 ) {
pixelType = mitk::MakePixelType<unsigned short, unsigned short >(nrComponents);
}
else
{
mitkThrow()<< "unknown scalar PixelType";
}
} else if(nrComponents == 3 && dimensions == 2) {
if( pythonPixeltype.compare("float64") == 0 ) {
pixelType = mitk::MakePixelType<DoubleRGBImageType>();
} else if( pythonPixeltype.compare("float32") == 0 ) {
pixelType = mitk::MakePixelType<FloatRGBImageType>();
} else if( pythonPixeltype.compare("uint8") == 0 ) {
pixelType = mitk::MakePixelType<UCRGBImageType>();
} else if( pythonPixeltype.compare("uint16") == 0 ) {
pixelType = mitk::MakePixelType<USRGBImageType>();
}
} else if( (nrComponents == 4) && dimensions == 2 ) {
if( pythonPixeltype.compare("float64") == 0 ) {
pixelType = mitk::MakePixelType<DoubleRGBAImageType>();
} else if( pythonPixeltype.compare("float32") == 0 ) {
pixelType = mitk::MakePixelType<FloatRGBAImageType>();
} else if( pythonPixeltype.compare("uint8") == 0 ) {
pixelType = mitk::MakePixelType<UCRGBAImageType>();
} else if( pythonPixeltype.compare("uint16") == 0 ) {
pixelType = mitk::MakePixelType<USRGBAImageType>();
}
}
else {
if( pythonPixeltype.compare("float64") == 0 ) {
pixelType = mitk::MakePixelType<double, itk::Vector<double,3> >(nrComponents);
} else if( pythonPixeltype.compare("float32") == 0 ) {
pixelType = mitk::MakePixelType<float, itk::Vector<float,3> >(nrComponents);
} else if( pythonPixeltype.compare("int16") == 0) {
pixelType = mitk::MakePixelType<short, itk::Vector<short,3> >(nrComponents);
} else if( pythonPixeltype.compare("int8") == 0 ) {
pixelType = mitk::MakePixelType<char, itk::Vector<char,3> >(nrComponents);
} else if( pythonPixeltype.compare("int32") == 0 ) {
pixelType = mitk::MakePixelType<int, itk::Vector<int,3> >(nrComponents);
} else if( pythonPixeltype.compare("int64") == 0 ) {
pixelType = mitk::MakePixelType<long, itk::Vector<long,3> >(nrComponents);
} else if( pythonPixeltype.compare("uint8") == 0 ) {
pixelType = mitk::MakePixelType<unsigned char, itk::Vector<unsigned char,3> >(nrComponents);
} else if( pythonPixeltype.compare("uint16") == 0 ) {
pixelType = mitk::MakePixelType<unsigned short, itk::Vector<unsigned short,3> >(nrComponents);
} else if( pythonPixeltype.compare("uint32") == 0 ) {
pixelType = mitk::MakePixelType<unsigned int, itk::Vector<unsigned int,3> >(nrComponents);
} else if( pythonPixeltype.compare("uint64") == 0 ) {
pixelType = mitk::MakePixelType<unsigned long, itk::Vector<unsigned long,3> >(nrComponents);
} else {
mitkThrow()<< "unknown vectorial PixelType";
}
}
return pixelType;
}
mitk::Image::Pointer mitk::PythonService::CopySimpleItkImageFromPython(const std::string &stdvarName)
{
double*ds = nullptr;
// access python module
PyObject *pyMod = PyImport_AddModule("__main__");
// global dictionarry
PyObject *pyDict = PyModule_GetDict(pyMod);
mitk::Image::Pointer mitkImage = mitk::Image::New();
mitk::Vector3D spacing;
mitk::Point3D origin;
QString command;
QString varName = QString::fromStdString( stdvarName );
command.append( QString("%1_numpy_array = sitk.GetArrayFromImage(%1)\n").arg(varName) );
command.append( QString("%1_spacing = numpy.asarray(%1.GetSpacing())\n").arg(varName) );
command.append( QString("%1_origin = numpy.asarray(%1.GetOrigin())\n").arg(varName) );
command.append( QString("%1_dtype = %1_numpy_array.dtype.name\n").arg(varName) );
command.append( QString("%1_direction = numpy.asarray(%1.GetDirection())\n").arg(varName) );
command.append( QString("%1_nrComponents = numpy.asarray(%1.GetNumberOfComponentsPerPixel())\n").arg(varName));
command.append( QString("%1_dtype = %1_numpy_array.dtype.name\n").arg(varName) );
MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString();
this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND );
PyObject* py_dtype = PyDict_GetItemString(pyDict,QString("%1_dtype").arg(varName).toStdString().c_str() );
std::string dtype = PyString_AsString(py_dtype);
PyArrayObject* py_data = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_numpy_array").arg(varName).toStdString().c_str() );
PyArrayObject* py_spacing = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_spacing").arg(varName).toStdString().c_str() );
PyArrayObject* py_origin = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_origin").arg(varName).toStdString().c_str() );
PyArrayObject* py_direction = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_direction").arg(varName).toStdString().c_str() );
PyArrayObject* py_nrComponents = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_nrComponents").arg(varName).toStdString().c_str() );
unsigned int nr_Components = *(reinterpret_cast<unsigned int*>(PyArray_DATA(py_nrComponents)));
unsigned int nr_dimensions = PyArray_NDIM(py_data);
if (nr_Components > 1) // for VectorImages the last dimension in the numpy array are the vector components.
{
--nr_dimensions;
}
mitk::PixelType pixelType = DeterminePixelType(dtype, nr_Components, nr_dimensions);
unsigned int* dimensions = new unsigned int[nr_dimensions];
// fill backwards , nd data saves dimensions in opposite direction
for( unsigned i = 0; i < nr_dimensions; ++i )
{
dimensions[i] = PyArray_DIMS(py_data)[nr_dimensions - 1 - i];
}
mitkImage->Initialize(pixelType, nr_dimensions, dimensions);
mitkImage->SetChannel(PyArray_DATA(py_data));
ds = reinterpret_cast<double*>(PyArray_DATA(py_spacing));
spacing[0] = ds[0];
spacing[1] = ds[1];
spacing[2] = ds[2];
mitkImage->GetGeometry()->SetSpacing(spacing);
ds = reinterpret_cast<double*>(PyArray_DATA(py_origin));
origin[0] = ds[0];
origin[1] = ds[1];
origin[2] = ds[2];
mitkImage->GetGeometry()->SetOrigin(origin);
itk::Matrix<double,3,3> py_transform;
ds = reinterpret_cast<double*>(PyArray_DATA(py_direction));
py_transform[0][0] = ds[0];
py_transform[0][1] = ds[1];
py_transform[0][2] = ds[2];
py_transform[1][0] = ds[3];
py_transform[1][1] = ds[4];
py_transform[1][2] = ds[5];
py_transform[2][0] = ds[6];
py_transform[2][1] = ds[7];
py_transform[2][2] = ds[8];
mitk::AffineTransform3D::Pointer affineTransform = mitkImage->GetGeometry()->GetIndexToWorldTransform();
itk::Matrix<double,3,3> transform = py_transform * affineTransform->GetMatrix();
affineTransform->SetMatrix(transform);
mitkImage->GetGeometry()->SetIndexToWorldTransform(affineTransform);
// mitk::AffineTransform3D::New();
//mitkImage->GetGeometry()->SetIndexToWorldTransform();
// cleanup
command.clear();
command.append( QString("del %1_numpy_array\n").arg(varName) );
command.append( QString("del %1_dtype\n").arg(varName) );
command.append( QString("del %1_spacing\n").arg(varName) );
command.append( QString("del %1_origin\n").arg(varName) );
command.append( QString("del %1_direction\n").arg(varName) );
command.append( QString("del %1_nrComponents\n").arg(varName) );
MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString();
this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND );
delete[] dimensions;
return mitkImage;
}
bool mitk::PythonService::CopyToPythonAsCvImage( mitk::Image* image, const std::string& stdvarName )
{
QString varName = QString::fromStdString( stdvarName );
QString command;
unsigned int* imgDim = image->GetDimensions();
int npy_nd = 1;
// access python module
PyObject *pyMod = PyImport_AddModule((char*)"__main__");
// global dictionary
PyObject *pyDict = PyModule_GetDict(pyMod);
mitk::PixelType pixelType = image->GetPixelType();
PyObject* npyArray = nullptr;
mitk::ImageReadAccessor racc(image);
void* array = (void*) racc.GetData();
// save the total number of elements here (since the numpy array is one dimensional)
npy_intp* npy_dims = new npy_intp[1];
npy_dims[0] = imgDim[0];
/**
* Build a string in the format [1024,1028,1]
* to describe the dimensionality. This is needed for simple itk
* to know the dimensions of the image
*/
QString dimensionString;
dimensionString.append(QString("["));
dimensionString.append(QString::number(imgDim[0]));
// ToDo: check if we need this
for (unsigned i = 1; i < 3; ++i)
// always three because otherwise the 3d-geometry gets destroyed
// (relevant for backtransformation of simple itk image to mitk.
{
dimensionString.append(QString(","));
dimensionString.append(QString::number(imgDim[i]));
npy_dims[0] *= imgDim[i];
}
dimensionString.append("]");
// the next line is necessary for vectorimages
npy_dims[0] *= pixelType.GetNumberOfComponents();
// default pixeltype: unsigned short
NPY_TYPES npy_type = NPY_USHORT;
- if( pixelType.GetComponentType() == itk::ImageIOBase::DOUBLE ) {
+ if( pixelType.GetComponentType() == itk::IOComponentEnum::DOUBLE ) {
npy_type = NPY_DOUBLE;
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::FLOAT ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::FLOAT ) {
npy_type = NPY_FLOAT;
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::SHORT) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::SHORT) {
npy_type = NPY_SHORT;
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::CHAR ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::CHAR ) {
npy_type = NPY_BYTE;
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::INT ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::INT ) {
npy_type = NPY_INT;
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::LONG ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::LONG ) {
npy_type = NPY_LONG;
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::UCHAR ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::UCHAR ) {
npy_type = NPY_UBYTE;
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::UINT ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::UINT ) {
npy_type = NPY_UINT;
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::ULONG ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::ULONG ) {
npy_type = NPY_LONG;
- } else if( pixelType.GetComponentType() == itk::ImageIOBase::USHORT ) {
+ } else if( pixelType.GetComponentType() == itk::IOComponentEnum::USHORT ) {
npy_type = NPY_USHORT;
}
else {
MITK_WARN << "not a recognized pixeltype";
return false;
}
// creating numpy array
import_array1 (true);
npyArray = PyArray_SimpleNewFromData(npy_nd,npy_dims,npy_type,array);
// add temp array it to the python dictionary to access it in python code
const int status = PyDict_SetItemString( pyDict,QString("%1_numpy_array")
.arg(varName).toStdString().c_str(),
npyArray );
// sanity check
if ( status != 0 )
return false;
command.append( QString("import numpy as np\n"));
//command.append( QString("if '%1' in globals():\n").arg(varName));
//command.append( QString(" del %1\n").arg(varName));
command.append( QString("%1_array_tmp=%1_numpy_array.copy()\n").arg(varName));
command.append( QString("%1_array_tmp=%1_array_tmp.reshape(%2,%3,%4)\n").arg( varName,
QString::number(imgDim[1]),
QString::number(imgDim[0]),
QString::number(pixelType.GetNumberOfComponents())));
command.append( QString("%1 = %1_array_tmp[:,...,::-1]\n").arg(varName));
command.append( QString("del %1_numpy_array\n").arg(varName) );
command.append( QString("del %1_array_tmp").arg(varName) );
MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString();
this->Execute( command.toStdString(), IPythonService::MULTI_LINE_COMMAND );
return true;
}
mitk::Image::Pointer mitk::PythonService::CopyCvImageFromPython( const std::string& stdvarName )
{
// access python module
PyObject *pyMod = PyImport_AddModule((char*)"__main__");
// global dictionarry
PyObject *pyDict = PyModule_GetDict(pyMod);
mitk::Image::Pointer mitkImage = mitk::Image::New();
QString command;
QString varName = QString::fromStdString( stdvarName );
command.append( QString("import numpy as np\n"));
command.append( QString("%1_dtype=%1.dtype.name\n").arg(varName) );
command.append( QString("%1_shape=np.asarray(%1.shape)\n").arg(varName) );
command.append( QString("%1_np_array=%1[:,...,::-1]\n").arg(varName));
command.append( QString("%1_np_array=np.reshape(%1_np_array,%1.shape[0] * %1.shape[1] * %1.shape[2])").arg(varName) );
MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString();
this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND );
PyObject* py_dtype = PyDict_GetItemString(pyDict,QString("%1_dtype").arg(varName).toStdString().c_str() );
std::string dtype = PyString_AsString(py_dtype);
PyArrayObject* py_data = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_np_array").arg(varName).toStdString().c_str() );
PyArrayObject* shape = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_shape").arg(varName).toStdString().c_str() );
size_t* d = reinterpret_cast<size_t*>(PyArray_DATA(shape));
unsigned int dimensions[3];
dimensions[0] = d[1];
dimensions[1] = d[0];
dimensions[2] = d[2];
unsigned int nr_dimensions = 2;
// get number of components
unsigned int nr_Components = (unsigned int) d[2];
auto pixelType = DeterminePixelType(dtype, nr_Components, nr_dimensions);
mitkImage->Initialize(pixelType, nr_dimensions, dimensions);
//mitkImage->SetChannel(py_data->data);
{
mitk::ImageWriteAccessor ra(mitkImage);
char* data = (char*)(ra.GetData());
memcpy(data, PyArray_DATA(py_data), dimensions[0] * dimensions[1] * pixelType.GetSize());
}
command.clear();
command.append( QString("del %1_shape\n").arg(varName) );
command.append( QString("del %1_dtype\n").arg(varName) );
command.append( QString("del %1_np_array").arg(varName));
MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString();
this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND );
return mitkImage;
}
ctkAbstractPythonManager *mitk::PythonService::GetPythonManager()
{
return &m_PythonManager;
}
mitk::Surface::Pointer mitk::PythonService::CopyVtkPolyDataFromPython( const std::string& stdvarName )
{
// access python module
PyObject *pyMod = PyImport_AddModule((char*)"__main__");
// global dictionarry
PyObject *pyDict = PyModule_GetDict(pyMod);
// python memory address
PyObject *pyAddr = nullptr;
// cpp address
size_t addr = 0;
mitk::Surface::Pointer surface = mitk::Surface::New();
QString command;
QString varName = QString::fromStdString( stdvarName );
command.append( QString("%1_addr_str = %1.GetAddressAsString(\"vtkPolyData\")\n").arg(varName) );
// remove 0x from the address
command.append( QString("%1_addr = int(%1_addr_str[5:],16)").arg(varName) );
MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString();
this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND );
// get address of the object
pyAddr = PyDict_GetItemString(pyDict,QString("%1_addr").arg(varName).toStdString().c_str());
// convert to long
addr = PyInt_AsLong(pyAddr);
MITK_DEBUG << "Python object address: " << addr;
// get the object
vtkPolyData* poly = (vtkPolyData*)((void*)addr);
surface->SetVtkPolyData(poly);
// delete helper variables from python stack
command = "";
command.append( QString("del %1_addr_str\n").arg(varName) );
command.append( QString("del %1_addr").arg(varName) );
MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString();
this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND );
return surface;
}
bool mitk::PythonService::CopyToPythonAsVtkPolyData( mitk::Surface* surface, const std::string& stdvarName )
{
QString varName = QString::fromStdString( stdvarName );
std::ostringstream oss;
std::string addr = "";
QString command;
QString address;
oss << (void*) ( surface->GetVtkPolyData() );
// get the address
addr = oss.str();
// remove "0x"
address = QString::fromStdString(addr.substr(2));
command.append( QString("%1 = vtk.vtkPolyData(\"%2\")\n").arg(varName).arg(address) );
MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString();
this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND );
return true;
}
bool mitk::PythonService::IsSimpleItkPythonWrappingAvailable()
{
this->Execute( "import SimpleITK as sitk\n", IPythonService::SINGLE_LINE_COMMAND );
// directly access cpp lib
this->Execute( "import SimpleITK._SimpleITK as _SimpleITK\n", IPythonService::SINGLE_LINE_COMMAND );
m_ItkWrappingAvailable = !this->PythonErrorOccured();
// check for numpy
this->Execute( "import numpy\n", IPythonService::SINGLE_LINE_COMMAND );
if ( this->PythonErrorOccured() )
MITK_ERROR << "Numpy not found.";
m_ItkWrappingAvailable = !this->PythonErrorOccured();
return m_ItkWrappingAvailable;
}
bool mitk::PythonService::IsOpenCvPythonWrappingAvailable()
{
this->Execute( "import cv2\n", IPythonService::SINGLE_LINE_COMMAND );
m_OpenCVWrappingAvailable = !this->PythonErrorOccured();
return m_OpenCVWrappingAvailable;
}
bool mitk::PythonService::IsVtkPythonWrappingAvailable()
{
this->Execute( "import vtk", IPythonService::SINGLE_LINE_COMMAND );
//this->Execute( "print \"Using VTK version \" + vtk.vtkVersion.GetVTKVersion()\n", IPythonService::SINGLE_LINE_COMMAND );
m_VtkWrappingAvailable = !this->PythonErrorOccured();
return m_VtkWrappingAvailable;
}
bool mitk::PythonService::PythonErrorOccured() const
{
return m_ErrorOccured;
}
diff --git a/Modules/QtWidgets/include/QmitkDataStorageTableModel.h b/Modules/QtWidgets/include/QmitkDataStorageTableModel.h
index 0abd6382e4..67d76e2519 100644
--- a/Modules/QtWidgets/include/QmitkDataStorageTableModel.h
+++ b/Modules/QtWidgets/include/QmitkDataStorageTableModel.h
@@ -1,223 +1,223 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef QmitkDataStorageTableModel_h
#define QmitkDataStorageTableModel_h
#include <MitkQtWidgetsExports.h>
/// Own includes.
#include "mitkBaseProperty.h"
#include "mitkDataStorage.h"
#include "mitkNodePredicateBase.h"
#include "mitkWeakPointer.h"
/// Toolkit includes.
#include <QAbstractTableModel>
/// Forward declarations.
///
/// \ingroup QmitkModule
/// \class QmitkDataStorageTableModel
///
/// \brief A table model for a set of DataNodes defined by a predicate.
/// \todo make columns interchangeable, select which properties to show as columns
///
class MITKQTWIDGETS_EXPORT QmitkDataStorageTableModel : public QAbstractTableModel
{
Q_OBJECT
//#Ctors/Dtor
public:
///
/// Constructs a new QmitkDataStorageTableModel and sets a predicate that defines
/// this list.
/// \see setPredicate()
///
QmitkDataStorageTableModel(mitk::DataStorage::Pointer _DataStorage,
mitk::NodePredicateBase *_Predicate = nullptr,
QObject *parent = nullptr);
///
/// Standard dtor. Delete predicate, disconnect from DataStorage.
///
~QmitkDataStorageTableModel() override;
//# Public GETTER
public:
///
/// Get the DataStorage.
///
const mitk::DataStorage::Pointer GetDataStorage() const;
///
/// Get the predicate.
///
mitk::NodePredicateBase::Pointer GetPredicate() const;
///
/// Get node at a specific model index. Another way to implement this, is
/// by introducing a new role like "DateTreeNode" and capture
/// that in the data function.
///
mitk::DataNode::Pointer GetNode(const QModelIndex &index) const;
///
/// Overridden from QAbstractTableModel. Returns the header data at section
/// for given orientation and role.
///
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
///
/// Overridden from QAbstractTableModel. Returns what can be done
/// with an item.
///
Qt::ItemFlags flags(const QModelIndex &index) const override;
///
/// Overridden from QAbstractTableModel. Returns the node count.
///
int rowCount(const QModelIndex &parent) const override;
///
/// Overridden from QAbstractTableModel. Returns the number of features (columns) to display.
///
int columnCount(const QModelIndex &parent) const override;
///
/// Overridden from QAbstractTableModel. Returns the data at index for given role.
///
QVariant data(const QModelIndex &index, int role) const override;
//# Public SETTERS
public:
///
/// Sets the DataStorage.
///
void SetDataStorage(mitk::DataStorage::Pointer _DataStorage);
///
/// Sets the predicate. <b>QmitkDataStorageTableModel is owner of the predicate!</b>
///
void SetPredicate(mitk::NodePredicateBase *_Predicate);
///
/// Adds a node to this model.
/// There are two constraints for nodes in this model:
/// 1. If a predicate is set (not null) the node will be checked against it.
/// 2. The node has to have a data object (no one wants to see empty nodes).
/// Also adds event listeners to the node.
///
virtual void AddNode(const mitk::DataNode *node);
///
/// Removes a node from this model. Also removes any event listener from the node.
///
virtual void RemoveNode(const mitk::DataNode *node);
///
/// Returns a copy of the node-vector that is shown by this model
///
virtual std::vector<mitk::DataNode *> GetNodeSet() const;
///
/// \brief Called when a single property was changed.
/// The function searches through the list of nodes in this model for the changed
/// property. If the property was found a dataChanged signal is emitted forcing
/// all observing views to request the data again.
///
virtual void PropertyModified(const itk::Object *caller, const itk::EventObject &event);
///
/// Overridden from QAbstractTableModel. Sets data at index for given role.
///
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
///
/// \brief Reimplemented sort function from QAbstractTableModel to enable sorting on the table.
///
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
//#PROTECTED INNER CLASSES
protected:
///
/// \struct DataNodeCompareFunction
- /// \brief A struct that inherits from std::binary_function. You can use it in std::sort algorithm for sorting the
+ /// \brief A struct that you can use in std::sort algorithm for sorting the
/// node list elements.
///
- struct DataNodeCompareFunction : public std::binary_function<mitk::DataNode::Pointer, mitk::DataNode::Pointer, bool>
+ struct DataNodeCompareFunction
{
///
/// \brief Specifies field of the property with which it will be sorted.
///
enum CompareCriteria
{
CompareByName = 0,
CompareByClassName,
CompareByVisibility
};
///
/// \brief Specifies Ascending/descending ordering.
///
enum CompareOperator
{
Less = 0,
Greater
};
///
/// \brief Creates a PropertyDataSetCompareFunction. A CompareCriteria and a CompareOperator must be given.
///
DataNodeCompareFunction(CompareCriteria _CompareCriteria = CompareByName, CompareOperator _CompareOperator = Less);
///
/// \brief The reimplemented compare function.
///
bool operator()(const mitk::DataNode::Pointer &_Left, const mitk::DataNode::Pointer &_Right) const;
protected:
CompareCriteria m_CompareCriteria;
CompareOperator m_CompareOperator;
};
//#Protected SETTER
protected:
///
/// Called when DataStorage or Predicate changed. Resets whole model and reads all nodes
/// in again.
///
virtual void Reset();
//#Protected MEMBER VARIABLES
protected:
///
/// Pointer to the DataStorage from which the nodes are selected (remember: in BlueBerry there
/// might be more than one DataStorage).
/// Store it in a weak pointer. This is a GUI class which should not hold a strong reference
/// to any non-GUI Object.
///
mitk::WeakPointer<mitk::DataStorage> m_DataStorage;
///
/// Holds the predicate that defines this SubSet of Nodes. If m_Predicate
/// is nullptr all Nodes will be selected.
///
mitk::NodePredicateBase::Pointer m_Predicate;
///
/// Holds all selected Nodes.
///
std::vector<mitk::DataNode *> m_NodeSet;
///
/// \brief Maps a property to an observer tag.
///
std::map<mitk::BaseProperty *, unsigned long> m_NamePropertyModifiedObserverTags;
///
/// \brief Maps a property to an observer tag.
///
std::map<mitk::BaseProperty *, unsigned long> m_VisiblePropertyModifiedObserverTags;
///
/// Saves if this model is currently working on events to prevent endless event loops.
///
bool m_BlockEvents;
///
/// \brief The property is true when the property list is sorted in descending order.
///
bool m_SortDescending;
};
#endif
diff --git a/Modules/QtWidgets/include/QmitkPropertiesTableModel.h b/Modules/QtWidgets/include/QmitkPropertiesTableModel.h
index 92ad8fd103..8dc13e55ca 100644
--- a/Modules/QtWidgets/include/QmitkPropertiesTableModel.h
+++ b/Modules/QtWidgets/include/QmitkPropertiesTableModel.h
@@ -1,249 +1,249 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
/// Header guard.
#ifndef QmitkPropertiesTableModel_h
#define QmitkPropertiesTableModel_h
#include <MitkQtWidgetsExports.h>
//# Own includes
#include "mitkDataNode.h"
#include "mitkWeakPointer.h"
//# Toolkit includes
#include <QAbstractTableModel>
#include <string>
#include <vector>
//# Forward declarations
/**
* \ingroup QmitkModule
* \brief A table model for showing and editing mitk::Properties.
*
* \see QmitkPropertyDelegate
*/
class MITKQTWIDGETS_EXPORT QmitkPropertiesTableModel : public QAbstractTableModel
{
//# PUBLIC CTORS,DTOR,TYPEDEFS,CONSTANTS
public:
static const int PROPERTY_NAME_COLUMN = 0;
static const int PROPERTY_VALUE_COLUMN = 1;
///
/// Typedef for the complete Property Datastructure, which may be written as follows:
/// Name->(mitk::BaseProperty::Pointer)
///
typedef std::pair<std::string, mitk::BaseProperty::Pointer> PropertyDataSet;
///
/// Constructs a new QmitkDataStorageTableModel
/// and sets the DataNode for this TableModel.
QmitkPropertiesTableModel(QObject *parent = nullptr, mitk::PropertyList::Pointer _PropertyList = nullptr);
///
/// Standard dtor. Nothing to do here.
~QmitkPropertiesTableModel() override;
//# PUBLIC GETTER
public:
///
/// Returns the property list of this table model.
///
mitk::PropertyList::Pointer GetPropertyList() const;
///
/// Overwritten from QAbstractTableModel. Returns the flags what can be done with the items (view, edit, ...)
Qt::ItemFlags flags(const QModelIndex &index) const override;
///
/// Overwritten from QAbstractTableModel. Returns the flags what can be done with the items (view, edit, ...)
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
///
/// Overwritten from QAbstractTableModel. Returns the flags what can be done with the items (view, edit, ...)
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
///
/// Overwritten from QAbstractTableModel. Returns the flags what can be done with the items (view, edit, ...)
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
///
/// Overwritten from QAbstractTableModel. Returns the number of columns. That is usually two in this model:
/// the properties name and its value.
int columnCount(const QModelIndex &parent) const override;
//# PUBLIC SETTER
public:
///
/// Sets the Property List to show. Resets the whole model. If _PropertyList is nullptr the model is empty.
///
void SetPropertyList(mitk::PropertyList *_PropertyList);
///
/// \brief Gets called when the list is about to be deleted.
///
virtual void PropertyListDelete();
///
/// \brief Called when a single property was changed. Send a model changed event to the Qt-outer world.
///
virtual void PropertyModified(const itk::Object *caller, const itk::EventObject &event);
///
/// \brief Called when a single property was changed. Send a model changed event to the Qt-outer world.
///
virtual void PropertyDelete(const itk::Object *caller, const itk::EventObject &event);
///
/// \brief Set a keyword for filtering of properties. Only properties beginning with this string will be shown
///
virtual void SetFilterPropertiesKeyWord(std::string _FilterKeyWord);
///
/// Overridden from QAbstractTableModel. Sets data at index for given role.
///
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
///
/// \brief Reimplemented sort function from QAbstractTableModel to enable sorting on the table.
///
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
//#PROTECTED INNER CLASSES
protected:
///
/// \struct PropertyDataSetCompareFunction
- /// \brief A struct that inherits from std::binary_function. You can use it in std::sort algorithm for sorting the
+ /// \brief A struct that you can use in std::sort algorithm for sorting the
/// property list elements.
///
- struct PropertyDataSetCompareFunction : public std::binary_function<PropertyDataSet, PropertyDataSet, bool>
+ struct PropertyDataSetCompareFunction
{
///
/// \brief Specifies field of the property with which it will be sorted.
///
enum CompareCriteria
{
CompareByName = 0,
CompareByValue
};
///
/// \brief Specifies Ascending/descending ordering.
///
enum CompareOperator
{
Less = 0,
Greater
};
///
/// \brief Creates a PropertyDataSetCompareFunction. A CompareCriteria and a CompareOperator must be given.
///
PropertyDataSetCompareFunction(CompareCriteria _CompareCriteria = CompareByName,
CompareOperator _CompareOperator = Less);
///
/// \brief The reimplemented compare function.
///
bool operator()(const PropertyDataSet &_Left, const PropertyDataSet &_Right) const;
protected:
CompareCriteria m_CompareCriteria;
CompareOperator m_CompareOperator;
};
///
/// An unary function for selecting Properties in a vector by a key word.
///
- struct PropertyListElementFilterFunction : public std::unary_function<PropertyDataSet, bool>
+ struct PropertyListElementFilterFunction
{
PropertyListElementFilterFunction(const std::string &m_FilterKeyWord);
///
/// \brief The reimplemented compare function.
///
bool operator()(const PropertyDataSet &_Elem) const;
protected:
std::string m_FilterKeyWord;
};
//# PROTECTED GETTER
protected:
///
/// \brief Searches for the specified property and returns the row of the element in this QTableModel.
/// If any errors occur, the function returns -1.
///
int FindProperty(const mitk::BaseProperty *_Property) const;
//# PROTECTED SETTER
protected:
///
/// Adds a property dataset to the current selection.
/// When a property is added a modified and delete listener
/// is appended.
///
void AddSelectedProperty(PropertyDataSet &_PropertyDataSet);
///
/// Removes a property dataset from the current selection.
/// When a property is removed the modified and delete listener
/// are also removed.
///
void RemoveSelectedProperty(unsigned int _Index);
///
/// Reset is called when a new filter keyword is set or a new
/// PropertyList is set. First of all, all priorly selected
/// properties are removed. Then all properties to be
/// selected (specified by the keyword) are added to the selection.
///
void Reset();
//# PROTECTED MEMBERS
protected:
///
/// Holds the pointer to the properties list. Dont use smart pointers here. Instead: Listen
/// to the delete event.
mitk::WeakPointer<mitk::PropertyList> m_PropertyList;
///
/// Store the properties in a vector so that they may be sorted
std::vector<PropertyDataSet> m_SelectedProperties;
///
/// \brief Holds all tags of Modified Event Listeners. We need it to remove them again.
///
std::vector<unsigned long> m_PropertyModifiedObserverTags;
///
/// \brief Holds all tags of Modified Event Listeners. We need it to remove them again.
///
std::vector<unsigned long> m_PropertyDeleteObserverTags;
unsigned long m_PropertyListDeleteObserverTag;
///
/// \brief Indicates if this class should neglect all incoming events because
/// the class itself triggered the event (e.g. when a property was edited).
///
bool m_BlockEvents;
///
/// \brief The property is true when the property list is sorted in descending order.
///
bool m_SortDescending;
///
/// \brief If set to any value, only properties containing the specified keyword in their name will be shown.
///
std::string m_FilterKeyWord;
};
#endif /* QMITKPROPERTIESTABLEMODEL_H_ */
diff --git a/Modules/QtWidgets/src/QmitkIOUtil.cpp b/Modules/QtWidgets/src/QmitkIOUtil.cpp
index f25869f0e2..abda3c7626 100644
--- a/Modules/QtWidgets/src/QmitkIOUtil.cpp
+++ b/Modules/QtWidgets/src/QmitkIOUtil.cpp
@@ -1,586 +1,586 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "QmitkIOUtil.h"
#include "mitkCoreServices.h"
#include "mitkCustomMimeType.h"
#include "mitkFileReaderRegistry.h"
#include "mitkFileWriterRegistry.h"
#include "mitkIMimeTypeProvider.h"
#include "mitkMimeType.h"
#include <mitkCoreObjectFactory.h>
#include <mitkIOUtil.h>
#include "QmitkFileReaderOptionsDialog.h"
#include "QmitkFileWriterOptionsDialog.h"
// QT
#include <QDebug>
#include <QFileDialog>
#include <QMessageBox>
#include <QSet>
#include <QString>
// ITK
#include <itksys/SystemTools.hxx>
#include <algorithm>
struct QmitkIOUtil::Impl
{
struct ReaderOptionsDialogFunctor : public ReaderOptionsFunctorBase
{
bool operator()(LoadInfo &loadInfo) const override
{
QmitkFileReaderOptionsDialog dialog(loadInfo);
if (dialog.exec() == QDialog::Accepted)
{
return !dialog.ReuseOptions();
}
else
{
loadInfo.m_Cancel = true;
return true;
}
}
};
struct WriterOptionsDialogFunctor : public WriterOptionsFunctorBase
{
bool operator()(SaveInfo &saveInfo) const override
{
QmitkFileWriterOptionsDialog dialog(saveInfo);
if (dialog.exec() == QDialog::Accepted)
{
return !dialog.ReuseOptions();
}
else
{
saveInfo.m_Cancel = true;
return true;
}
}
};
//! Filename characters that are not valid - depending on the platform (Windows, Posix)
static QString s_InvalidFilenameCharacters;
//! Return 'true' when:
//! - the specified filename contains characters not accepted by the file system (see s_InvalidFilenameCharacters)
//! - filename starts or ends in space characters
//!
//! This test is not exhaustive but just excluding the most common problems.
static bool IsIllegalFilename(const QString &fullFilename)
{
QFileInfo fi(fullFilename);
auto filename = fi.fileName();
for (const auto &ch : qAsConst(s_InvalidFilenameCharacters))
{
if (filename.contains(ch))
{
return true;
}
}
if (filename.startsWith(' ' || filename.endsWith(' ')))
{
return true;
}
return false;
}
}; // Impl
#if defined(_WIN32) || defined(_WIN64)
QString QmitkIOUtil::Impl::s_InvalidFilenameCharacters = "<>:\"/\\|?*";
#else
QString QmitkIOUtil::Impl::s_InvalidFilenameCharacters = "/";
#endif
-struct MimeTypeComparison : public std::unary_function<mitk::MimeType, bool>
+struct MimeTypeComparison
{
MimeTypeComparison(const std::string &mimeTypeName) : m_Name(mimeTypeName) {}
bool operator()(const mitk::MimeType &mimeType) const { return mimeType.GetName() == m_Name; }
const std::string m_Name;
};
QString QmitkIOUtil::GetFileOpenFilterString()
{
QString filters;
mitk::CoreServicePointer<mitk::IMimeTypeProvider> mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider());
std::vector<std::string> categories = mimeTypeProvider->GetCategories();
for (std::vector<std::string>::iterator cat = categories.begin(); cat != categories.end(); ++cat)
{
QSet<QString> filterExtensions;
std::vector<mitk::MimeType> mimeTypes = mimeTypeProvider->GetMimeTypesForCategory(*cat);
for (std::vector<mitk::MimeType>::iterator mt = mimeTypes.begin(); mt != mimeTypes.end(); ++mt)
{
std::vector<std::string> extensions = mt->GetExtensions();
for (std::vector<std::string>::iterator ext = extensions.begin(); ext != extensions.end(); ++ext)
{
filterExtensions << QString::fromStdString(*ext);
}
}
QString filter = QString::fromStdString(*cat) + " (";
foreach (const QString &extension, filterExtensions)
{
filter += "*." + extension + " ";
}
filter = filter.replace(filter.size() - 1, 1, ')');
filters += ";;" + filter;
}
filters.prepend("All (*)");
return filters;
}
QList<mitk::BaseData::Pointer> QmitkIOUtil::Load(const QStringList &paths, QWidget *parent)
{
std::vector<LoadInfo> loadInfos;
foreach (const QString &file, paths)
{
loadInfos.push_back(LoadInfo(file.toLocal8Bit().constData()));
}
Impl::ReaderOptionsDialogFunctor optionsCallback;
std::string errMsg = Load(loadInfos, nullptr, nullptr, &optionsCallback);
if (!errMsg.empty())
{
QMessageBox::warning(parent, "Error reading files", QString::fromStdString(errMsg));
mitkThrow() << errMsg;
}
QList<mitk::BaseData::Pointer> qResult;
for (std::vector<LoadInfo>::const_iterator iter = loadInfos.begin(), iterEnd = loadInfos.end(); iter != iterEnd;
++iter)
{
for (const auto &elem : iter->m_Output)
{
qResult << elem;
}
}
return qResult;
}
mitk::DataStorage::SetOfObjects::Pointer QmitkIOUtil::Load(const QStringList &paths,
mitk::DataStorage &storage,
QWidget *parent)
{
std::vector<LoadInfo> loadInfos;
foreach (const QString &file, paths)
{
loadInfos.push_back(LoadInfo(file.toLocal8Bit().constData()));
}
mitk::DataStorage::SetOfObjects::Pointer nodeResult = mitk::DataStorage::SetOfObjects::New();
Impl::ReaderOptionsDialogFunctor optionsCallback;
std::string errMsg = Load(loadInfos, nodeResult, &storage, &optionsCallback);
if (!errMsg.empty())
{
QMessageBox::warning(parent, "Error reading files", QString::fromStdString(errMsg));
}
return nodeResult;
}
QList<mitk::BaseData::Pointer> QmitkIOUtil::Load(const QString &path, QWidget *parent)
{
QStringList paths;
paths << path;
return Load(paths, parent);
}
mitk::DataStorage::SetOfObjects::Pointer QmitkIOUtil::Load(const QString &path,
mitk::DataStorage &storage,
QWidget *parent)
{
QStringList paths;
paths << path;
return Load(paths, storage, parent);
}
QString QmitkIOUtil::Save(const mitk::BaseData *data,
const QString &defaultBaseName,
const QString &defaultPath,
QWidget *parent,
bool setPathProperty)
{
std::vector<const mitk::BaseData *> dataVector;
dataVector.push_back(data);
QStringList defaultBaseNames;
defaultBaseNames.push_back(defaultBaseName);
return Save(dataVector, defaultBaseNames, defaultPath, parent, setPathProperty).back();
}
QStringList QmitkIOUtil::Save(const std::vector<const mitk::BaseData *> &data,
const QStringList &defaultBaseNames,
const QString &defaultPath,
QWidget *parent,
bool setPathProperty)
{
QStringList fileNames;
QString currentPath = defaultPath;
std::vector<SaveInfo> saveInfos;
int counter = 0;
for (std::vector<const mitk::BaseData *>::const_iterator dataIter = data.begin(), dataIterEnd = data.end();
dataIter != dataIterEnd;
++dataIter, ++counter)
{
SaveInfo saveInfo(*dataIter, mitk::MimeType(), std::string());
SaveFilter filters(saveInfo);
// If there is only the "__all__" filter string, it means there is no writer for this base data
if (filters.Size() < 2)
{
QMessageBox::warning(
parent,
"Saving not possible",
QString("No writer available for type \"%1\"").arg(QString::fromStdString((*dataIter)->GetNameOfClass())));
continue;
}
// Construct a default path and file name
QString filterString = filters.ToString();
QString selectedFilter = filters.GetDefaultFilter();
QString fileName = currentPath;
QString dialogTitle = "Save " + QString::fromStdString((*dataIter)->GetNameOfClass());
if (counter < defaultBaseNames.size())
{
dialogTitle += " \"" + defaultBaseNames[counter] + "\"";
fileName += QDir::separator() + defaultBaseNames[counter];
// We do not append an extension to the file name by default. The extension
// is chosen by the user by either selecting a filter or writing the
// extension in the file name himself (in the file save dialog).
/*
QString defaultExt = filters.GetDefaultExtension();
if (!defaultExt.isEmpty())
{
fileName += "." + defaultExt;
}
*/
}
// Ask the user for a file name
QString nextName = QFileDialog::getSaveFileName(parent, dialogTitle, fileName, filterString, &selectedFilter);
if (Impl::IsIllegalFilename(nextName))
{
QMessageBox::warning(
parent,
"Saving not possible",
QString("File \"%2\" contains invalid characters.\n\nPlease avoid any of \"%1\"")
.arg(Impl::s_InvalidFilenameCharacters.split("", QString::SkipEmptyParts).join(" "))
.arg(nextName));
continue;
}
if (nextName.isEmpty())
{
// We stop asking for further file names, but we still save the
// data where the user already confirmed the save dialog.
break;
}
fileName = nextName;
std::string stdFileName = fileName.toLocal8Bit().constData();
QFileInfo fileInfo(fileName);
currentPath = fileInfo.absolutePath();
QString suffix = fileInfo.completeSuffix();
mitk::MimeType filterMimeType = filters.GetMimeTypeForFilter(selectedFilter);
mitk::MimeType selectedMimeType;
if (fileInfo.exists() && !fileInfo.isFile())
{
QMessageBox::warning(parent, "Saving not possible", QString("The path \"%1\" is not a file").arg(fileName));
continue;
}
// Theoretically, the user could have entered an extension that does not match the selected filter
// The extension then has prioritry over the filter
// Check if one of the available mime-types match the filename
std::vector<mitk::MimeType> filterMimeTypes = filters.GetMimeTypes();
for (std::vector<mitk::MimeType>::const_iterator mimeTypeIter = filterMimeTypes.begin(),
mimeTypeIterEnd = filterMimeTypes.end();
mimeTypeIter != mimeTypeIterEnd;
++mimeTypeIter)
{
if (mimeTypeIter->MatchesExtension(stdFileName))
{
selectedMimeType = *mimeTypeIter;
break;
}
}
if (!selectedMimeType.IsValid())
{
// The file name either does not contain an extension or the
// extension is unknown.
// If the file already exists, we stop here because we are unable
// to (over)write the file without adding a custom suffix. If the file
// does not exist, we add the default extension from the currently
// selected filter. If the "All" filter was selected, we only add the
// default extensions if the file name itself does not already contain
// an extension.
if (!fileInfo.exists())
{
if (filterMimeType == SaveFilter::ALL_MIMETYPE())
{
if (suffix.isEmpty())
{
// Use the highest ranked mime-type from the list
selectedMimeType = filters.GetDefaultMimeType();
}
}
else
{
selectedMimeType = filterMimeType;
}
if (selectedMimeType.IsValid())
{
suffix = QString::fromStdString(selectedMimeType.GetExtensions().front());
fileName += "." + suffix;
stdFileName = fileName.toLocal8Bit().constData();
// We changed the file name (added a suffix) so ask in case
// the file aready exists.
fileInfo = QFileInfo(fileName);
if (fileInfo.exists())
{
if (!fileInfo.isFile())
{
QMessageBox::warning(
parent, "Saving not possible", QString("The path \"%1\" is not a file").arg(fileName));
continue;
}
if (QMessageBox::question(
parent,
"Replace File",
QString("A file named \"%1\" already exists. Do you want to replace it?").arg(fileName)) ==
QMessageBox::No)
{
continue;
}
}
}
}
}
if (!selectedMimeType.IsValid())
{
// The extension/filename is not valid (no mime-type found), bail out
QMessageBox::warning(
parent, "Saving not possible", QString("No mime-type available which can handle \"%1\".").arg(fileName));
continue;
}
if (!QFileInfo(fileInfo.absolutePath()).isWritable())
{
QMessageBox::warning(parent, "Saving not possible", QString("The path \"%1\" is not writable").arg(fileName));
continue;
}
fileNames.push_back(fileName);
saveInfo.m_Path = stdFileName;
saveInfo.m_MimeType = selectedMimeType;
// pre-select the best writer for the chosen mime-type
saveInfo.m_WriterSelector.Select(selectedMimeType.GetName());
saveInfos.push_back(saveInfo);
}
if (!saveInfos.empty())
{
Impl::WriterOptionsDialogFunctor optionsCallback;
std::string errMsg = Save(saveInfos, &optionsCallback, setPathProperty);
if (!errMsg.empty())
{
QMessageBox::warning(parent, "Error writing files", QString::fromStdString(errMsg));
mitkThrow() << errMsg;
}
}
return fileNames;
}
void QmitkIOUtil::SaveBaseDataWithDialog(mitk::BaseData *data, std::string fileName, QWidget * /*parent*/)
{
Save(data, fileName);
}
void QmitkIOUtil::SaveSurfaceWithDialog(mitk::Surface::Pointer surface, std::string fileName, QWidget * /*parent*/)
{
Save(surface, fileName);
}
void QmitkIOUtil::SaveImageWithDialog(mitk::Image::Pointer image, std::string fileName, QWidget * /*parent*/)
{
Save(image, fileName);
}
void QmitkIOUtil::SavePointSetWithDialog(mitk::PointSet::Pointer pointset, std::string fileName, QWidget * /*parent*/)
{
Save(pointset, fileName);
}
struct QmitkIOUtil::SaveFilter::Impl
{
Impl(const mitk::IOUtil::SaveInfo &saveInfo) : m_SaveInfo(saveInfo)
{
// Add an artifical filter for "All"
m_MimeTypes.push_back(ALL_MIMETYPE());
m_FilterStrings.push_back("All (*.*)");
// Get all writers and their mime types for the given base data type
// (this is sorted already)
std::vector<mitk::MimeType> mimeTypes = saveInfo.m_WriterSelector.GetMimeTypes();
for (std::vector<mitk::MimeType>::const_reverse_iterator iter = mimeTypes.rbegin(), iterEnd = mimeTypes.rend();
iter != iterEnd;
++iter)
{
QList<QString> filterExtensions;
mitk::MimeType mimeType = *iter;
std::vector<std::string> extensions = mimeType.GetExtensions();
for (auto &extension : extensions)
{
filterExtensions << QString::fromStdString(extension);
}
if (m_DefaultExtension.isEmpty())
{
m_DefaultExtension = QString::fromStdString(extensions.front());
}
QString filter = QString::fromStdString(mimeType.GetComment()) + " (";
foreach (const QString &extension, filterExtensions)
{
filter += "*." + extension + " ";
}
filter = filter.replace(filter.size() - 1, 1, ')');
m_MimeTypes.push_back(mimeType);
m_FilterStrings.push_back(filter);
}
}
const mitk::IOUtil::SaveInfo m_SaveInfo;
std::vector<mitk::MimeType> m_MimeTypes;
QStringList m_FilterStrings;
QString m_DefaultExtension;
};
mitk::MimeType QmitkIOUtil::SaveFilter::ALL_MIMETYPE()
{
static mitk::CustomMimeType allMimeType(std::string("__all__"));
return mitk::MimeType(allMimeType, -1, -1);
}
QmitkIOUtil::SaveFilter::SaveFilter(const QmitkIOUtil::SaveFilter &other) : d(new Impl(*other.d))
{
}
QmitkIOUtil::SaveFilter::SaveFilter(const SaveInfo &saveInfo) : d(new Impl(saveInfo))
{
}
QmitkIOUtil::SaveFilter &QmitkIOUtil::SaveFilter::operator=(const QmitkIOUtil::SaveFilter &other)
{
d.reset(new Impl(*other.d));
return *this;
}
std::vector<mitk::MimeType> QmitkIOUtil::SaveFilter::GetMimeTypes() const
{
return d->m_MimeTypes;
}
QString QmitkIOUtil::SaveFilter::GetFilterForMimeType(const std::string &mimeType) const
{
std::vector<mitk::MimeType>::const_iterator iter =
std::find_if(d->m_MimeTypes.begin(), d->m_MimeTypes.end(), MimeTypeComparison(mimeType));
if (iter == d->m_MimeTypes.end())
{
return QString();
}
int index = static_cast<int>(iter - d->m_MimeTypes.begin());
if (index < 0 || index >= d->m_FilterStrings.size())
{
return QString();
}
return d->m_FilterStrings[index];
}
mitk::MimeType QmitkIOUtil::SaveFilter::GetMimeTypeForFilter(const QString &filter) const
{
int index = d->m_FilterStrings.indexOf(filter);
if (index < 0)
{
return mitk::MimeType();
}
return d->m_MimeTypes[index];
}
QString QmitkIOUtil::SaveFilter::GetDefaultFilter() const
{
if (d->m_FilterStrings.size() > 1)
{
return d->m_FilterStrings.at(1);
}
else if (d->m_FilterStrings.size() > 0)
{
return d->m_FilterStrings.front();
}
return QString();
}
QString QmitkIOUtil::SaveFilter::GetDefaultExtension() const
{
return d->m_DefaultExtension;
}
mitk::MimeType QmitkIOUtil::SaveFilter::GetDefaultMimeType() const
{
if (d->m_MimeTypes.size() > 1)
{
return d->m_MimeTypes.at(1);
}
else if (d->m_MimeTypes.size() > 0)
{
return d->m_MimeTypes.front();
}
return mitk::MimeType();
}
QString QmitkIOUtil::SaveFilter::ToString() const
{
return d->m_FilterStrings.join(";;");
}
int QmitkIOUtil::SaveFilter::Size() const
{
return d->m_FilterStrings.size();
}
bool QmitkIOUtil::SaveFilter::IsEmpty() const
{
return d->m_FilterStrings.isEmpty();
}
bool QmitkIOUtil::SaveFilter::ContainsMimeType(const std::string &mimeType)
{
return std::find_if(d->m_MimeTypes.begin(), d->m_MimeTypes.end(), MimeTypeComparison(mimeType)) !=
d->m_MimeTypes.end();
}
diff --git a/Modules/QtWidgetsExt/src/QmitkAboutDialog.cpp b/Modules/QtWidgetsExt/src/QmitkAboutDialog.cpp
index b29b0e411b..43b0aa316b 100644
--- a/Modules/QtWidgetsExt/src/QmitkAboutDialog.cpp
+++ b/Modules/QtWidgetsExt/src/QmitkAboutDialog.cpp
@@ -1,83 +1,82 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "QmitkAboutDialog.h"
#include "QmitkModulesDialog.h"
#include <QPushButton>
#include <itkConfigure.h>
#include <mitkVersion.h>
-#include <vtkConfigure.h>
#include <vtkVersionMacros.h>
QmitkAboutDialog::QmitkAboutDialog(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f)
{
m_GUI.setupUi(this);
QString mitkRevision(MITK_REVISION);
QString mitkRevisionDescription(MITK_REVISION_DESC);
QString itkVersion = QString("%1.%2.%3").arg(ITK_VERSION_MAJOR).arg(ITK_VERSION_MINOR).arg(ITK_VERSION_PATCH);
QString vtkVersion = QString("%1.%2.%3").arg(VTK_MAJOR_VERSION).arg(VTK_MINOR_VERSION).arg(VTK_BUILD_VERSION);
QString revisionText = QString("Revision: %1").arg(MITK_REVISION);
if (!QString(MITK_REVISION_DESC).isEmpty())
revisionText += QString("\nDescription: %1").arg(MITK_REVISION_DESC);
m_GUI.m_RevisionLabel->setText(revisionText);
m_GUI.m_ToolkitVersionsLabel->setText(QString("ITK %1, VTK %2, Qt %3").arg(itkVersion, vtkVersion, QT_VERSION_STR));
QPushButton *btnModules = new QPushButton(QIcon(":/QtWidgetsExt/ModuleView.png"), "Modules");
m_GUI.m_ButtonBox->addButton(btnModules, QDialogButtonBox::ActionRole);
connect(btnModules, SIGNAL(clicked()), this, SLOT(ShowModules()));
connect(m_GUI.m_ButtonBox, SIGNAL(rejected()), this, SLOT(reject()));
}
QmitkAboutDialog::~QmitkAboutDialog()
{
}
void QmitkAboutDialog::ShowModules()
{
QmitkModulesDialog dialog(this);
dialog.exec();
}
QString QmitkAboutDialog::GetAboutText() const
{
return m_GUI.m_AboutLabel->text();
}
QString QmitkAboutDialog::GetCaptionText() const
{
return m_GUI.m_CaptionLabel->text();
}
QString QmitkAboutDialog::GetRevisionText() const
{
return m_GUI.m_RevisionLabel->text();
}
void QmitkAboutDialog::SetAboutText(const QString &text)
{
m_GUI.m_AboutLabel->setText(text);
}
void QmitkAboutDialog::SetCaptionText(const QString &text)
{
m_GUI.m_CaptionLabel->setText(text);
}
void QmitkAboutDialog::SetRevisionText(const QString &text)
{
m_GUI.m_RevisionLabel->setText(text);
}
diff --git a/Modules/RT/src/mitkDoseImageVtkMapper2D.cpp b/Modules/RT/src/mitkDoseImageVtkMapper2D.cpp
index 65be0f3e67..1bbfcf7867 100644
--- a/Modules/RT/src/mitkDoseImageVtkMapper2D.cpp
+++ b/Modules/RT/src/mitkDoseImageVtkMapper2D.cpp
@@ -1,1163 +1,1163 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// MITK
#include "mitkImageStatisticsHolder.h"
#include "mitkPlaneClipping.h"
#include "mitkPropertyNameHelper.h"
#include <mitkAbstractTransformGeometry.h>
#include <mitkDataNode.h>
#include <mitkImageSliceSelector.h>
#include <mitkIsoDoseLevelSetProperty.h>
#include <mitkIsoDoseLevelVectorProperty.h>
#include <mitkLevelWindowProperty.h>
#include <mitkLookupTableProperty.h>
#include <mitkPixelType.h>
#include <mitkPlaneGeometry.h>
#include <mitkProperties.h>
#include <mitkRTConstants.h>
#include <mitkRenderingModeProperty.h>
#include <mitkResliceMethodProperty.h>
#include <mitkTransferFunctionProperty.h>
#include <mitkVtkResliceInterpolationProperty.h>
// MITK Rendering
#include "mitkDoseImageVtkMapper2D.h"
#include "vtkMitkLevelWindowFilter.h"
#include "vtkMitkThickSlicesFilter.h"
#include "vtkNeverTranslucentTexture.h"
// VTK
#include <vtkCamera.h>
#include <vtkCellData.h>
#include <vtkColorTransferFunction.h>
#include <vtkGeneralTransform.h>
#include <vtkImageChangeInformation.h>
#include <vtkImageData.h>
#include <vtkImageExtractComponents.h>
#include <vtkImageReslice.h>
#include <vtkLookupTable.h>
#include <vtkMatrix4x4.h>
#include <vtkPlaneSource.h>
#include <vtkPoints.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkTransform.h>
#include <vtkUnsignedCharArray.h>
// ITK
#include <itkRGBAPixel.h>
mitk::DoseImageVtkMapper2D::DoseImageVtkMapper2D()
{
}
mitk::DoseImageVtkMapper2D::~DoseImageVtkMapper2D()
{
// The 3D RW Mapper (PlaneGeometryDataVtkMapper3D) is listening to this event,
// in order to delete the images from the 3D RW.
this->InvokeEvent(itk::DeleteEvent());
}
// set the two points defining the textured plane according to the dimension and spacing
void mitk::DoseImageVtkMapper2D::GeneratePlane(mitk::BaseRenderer *renderer, double planeBounds[6])
{
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
float depth = this->CalculateLayerDepth(renderer);
// Set the origin to (xMin; yMin; depth) of the plane. This is necessary for obtaining the correct
// plane size in crosshair rotation and swivel mode.
localStorage->m_Plane->SetOrigin(planeBounds[0], planeBounds[2], depth);
// These two points define the axes of the plane in combination with the origin.
// Point 1 is the x-axis and point 2 the y-axis.
// Each plane is transformed according to the view (axial, coronal and saggital) afterwards.
localStorage->m_Plane->SetPoint1(planeBounds[1], planeBounds[2], depth); // P1: (xMax, yMin, depth)
localStorage->m_Plane->SetPoint2(planeBounds[0], planeBounds[3], depth); // P2: (xMin, yMax, depth)
}
float mitk::DoseImageVtkMapper2D::CalculateLayerDepth(mitk::BaseRenderer *renderer)
{
// get the clipping range to check how deep into z direction we can render images
double maxRange = renderer->GetVtkRenderer()->GetActiveCamera()->GetClippingRange()[1];
// Due to a VTK bug, we cannot use the whole clipping range. /100 is empirically determined
float depth = -maxRange * 0.01; // divide by 100
int layer = 0;
GetDataNode()->GetIntProperty("layer", layer, renderer);
// add the layer property for each image to render images with a higher layer on top of the others
depth += layer * 10; //*10: keep some room for each image (e.g. for ODFs in between)
if (depth > 0.0f)
{
depth = 0.0f;
MITK_WARN << "Layer value exceeds clipping range. Set to minimum instead.";
}
return depth;
}
const mitk::Image *mitk::DoseImageVtkMapper2D::GetInput(void)
{
return static_cast<const mitk::Image *>(GetDataNode()->GetData());
}
vtkProp *mitk::DoseImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer)
{
// return the actor corresponding to the renderer
return m_LSH.GetLocalStorage(renderer)->m_Actors;
}
void mitk::DoseImageVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
mitk::Image *input = const_cast<mitk::Image *>(this->GetInput());
mitk::DataNode *datanode = this->GetDataNode();
if (input == nullptr || input->IsInitialized() == false)
{
return;
}
// check if there is a valid worldGeometry
const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry();
if ((worldGeometry == nullptr) || (!worldGeometry->IsValid()) || (!worldGeometry->HasReferenceGeometry()))
{
return;
}
input->Update();
// early out if there is no intersection of the current rendering geometry
// and the geometry of the image that is to be rendered.
if (!RenderingGeometryIntersectsImage(worldGeometry, input->GetSlicedGeometry()))
{
// set image to nullptr, to clear the texture in 3D, because
// the latest image is used there if the plane is out of the geometry
// see bug-13275
localStorage->m_ReslicedImage = nullptr;
localStorage->m_Mapper->SetInputData(localStorage->m_EmptyPolyData);
return;
}
// set main input for ExtractSliceFilter
localStorage->m_Reslicer->SetInput(input);
localStorage->m_Reslicer->SetWorldGeometry(worldGeometry);
localStorage->m_Reslicer->SetTimeStep(this->GetTimestep());
// set the transformation of the image to adapt reslice axis
localStorage->m_Reslicer->SetResliceTransformByGeometry(
input->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep()));
// is the geometry of the slice based on the input image or the worldgeometry?
bool inPlaneResampleExtentByGeometry = false;
datanode->GetBoolProperty("in plane resample extent by geometry", inPlaneResampleExtentByGeometry, renderer);
localStorage->m_Reslicer->SetInPlaneResampleExtentByGeometry(inPlaneResampleExtentByGeometry);
// Initialize the interpolation mode for resampling; switch to nearest
// neighbor if the input image is too small.
if ((input->GetDimension() >= 3) && (input->GetDimension(2) > 1))
{
VtkResliceInterpolationProperty *resliceInterpolationProperty;
datanode->GetProperty(resliceInterpolationProperty, "reslice interpolation");
int interpolationMode = VTK_RESLICE_NEAREST;
if (resliceInterpolationProperty != nullptr)
{
interpolationMode = resliceInterpolationProperty->GetInterpolation();
}
switch (interpolationMode)
{
case VTK_RESLICE_NEAREST:
localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST);
break;
case VTK_RESLICE_LINEAR:
localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_LINEAR);
break;
case VTK_RESLICE_CUBIC:
localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_CUBIC);
break;
}
}
else
{
localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST);
}
// set the vtk output property to true, makes sure that no unneeded mitk image convertion
// is done.
localStorage->m_Reslicer->SetVtkOutputRequest(true);
// Thickslicing
int thickSlicesMode = 0;
int thickSlicesNum = 1;
// Thick slices parameters
if (input->GetPixelType().GetNumberOfComponents() == 1) // for now only single component are allowed
{
DataNode *dn = renderer->GetCurrentWorldPlaneGeometryNode();
if (dn)
{
ResliceMethodProperty *resliceMethodEnumProperty = nullptr;
if (dn->GetProperty(resliceMethodEnumProperty, "reslice.thickslices") && resliceMethodEnumProperty)
thickSlicesMode = resliceMethodEnumProperty->GetValueAsId();
IntProperty *intProperty = nullptr;
if (dn->GetProperty(intProperty, "reslice.thickslices.num") && intProperty)
{
thickSlicesNum = intProperty->GetValue();
if (thickSlicesNum < 1)
thickSlicesNum = 1;
if (thickSlicesNum > 10)
thickSlicesNum = 10;
}
}
else
{
MITK_WARN << "no associated widget plane data tree node found";
}
}
const PlaneGeometry *planeGeometry = dynamic_cast<const PlaneGeometry *>(worldGeometry);
if (thickSlicesMode > 0)
{
double dataZSpacing = 1.0;
Vector3D normInIndex, normal;
if (planeGeometry != nullptr)
{
normal = planeGeometry->GetNormal();
}
else
{
const mitk::AbstractTransformGeometry *abstractGeometry =
dynamic_cast<const AbstractTransformGeometry *>(worldGeometry);
if (abstractGeometry != nullptr)
normal = abstractGeometry->GetPlane()->GetNormal();
else
return; // no fitting geometry set
}
normal.Normalize();
input->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())->WorldToIndex(normal, normInIndex);
dataZSpacing = 1.0 / normInIndex.GetNorm();
localStorage->m_Reslicer->SetOutputDimensionality(3);
localStorage->m_Reslicer->SetOutputSpacingZDirection(dataZSpacing);
localStorage->m_Reslicer->SetOutputExtentZDirection(-thickSlicesNum, 0 + thickSlicesNum);
// Do the reslicing. Modified() is called to make sure that the reslicer is
// executed even though the input geometry information did not change; this
// is necessary when the input /em data, but not the /em geometry changes.
localStorage->m_TSFilter->SetThickSliceMode(thickSlicesMode - 1);
localStorage->m_TSFilter->SetInputData(localStorage->m_Reslicer->GetVtkOutput());
// vtkFilter=>mitkFilter=>vtkFilter update mechanism will fail without calling manually
localStorage->m_Reslicer->Modified();
localStorage->m_Reslicer->Update();
localStorage->m_TSFilter->Modified();
localStorage->m_TSFilter->Update();
localStorage->m_ReslicedImage = localStorage->m_TSFilter->GetOutput();
}
else
{
// this is needed when thick mode was enable bevore. These variable have to be reset to default values
localStorage->m_Reslicer->SetOutputDimensionality(2);
localStorage->m_Reslicer->SetOutputSpacingZDirection(1.0);
localStorage->m_Reslicer->SetOutputExtentZDirection(0, 0);
localStorage->m_Reslicer->Modified();
// start the pipeline with updating the largest possible, needed if the geometry of the input has changed
localStorage->m_Reslicer->UpdateLargestPossibleRegion();
localStorage->m_ReslicedImage = localStorage->m_Reslicer->GetVtkOutput();
}
// Bounds information for reslicing (only reuqired if reference geometry
// is present)
// this used for generating a vtkPLaneSource with the right size
double sliceBounds[6];
for (int i = 0; i < 6; ++i)
{
sliceBounds[i] = 0.0;
}
localStorage->m_Reslicer->GetClippedPlaneBounds(sliceBounds);
// get the spacing of the slice
localStorage->m_mmPerPixel = localStorage->m_Reslicer->GetOutputSpacing();
// calculate minimum bounding rect of IMAGE in texture
{
double textureClippingBounds[6];
for (int i = 0; i < 6; ++i)
{
textureClippingBounds[i] = 0.0;
}
// Calculate the actual bounds of the transformed plane clipped by the
// dataset bounding box; this is required for drawing the texture at the
// correct position during 3D mapping.
mitk::PlaneClipping::CalculateClippedPlaneBounds(input->GetGeometry(), planeGeometry, textureClippingBounds);
textureClippingBounds[0] = static_cast<int>(textureClippingBounds[0] / localStorage->m_mmPerPixel[0] + 0.5);
textureClippingBounds[1] = static_cast<int>(textureClippingBounds[1] / localStorage->m_mmPerPixel[0] + 0.5);
textureClippingBounds[2] = static_cast<int>(textureClippingBounds[2] / localStorage->m_mmPerPixel[1] + 0.5);
textureClippingBounds[3] = static_cast<int>(textureClippingBounds[3] / localStorage->m_mmPerPixel[1] + 0.5);
// clipping bounds for cutting the image
localStorage->m_LevelWindowFilter->SetClippingBounds(textureClippingBounds);
}
// get the number of scalar components to distinguish between different image types
int numberOfComponents = localStorage->m_ReslicedImage->GetNumberOfScalarComponents();
// get the showIsoLines property
bool showIsoLines = false;
datanode->GetBoolProperty("dose.showIsoLines", showIsoLines, renderer);
if (showIsoLines) // contour rendering
{
// generate contours/outlines
localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer);
float binaryOutlineWidth(1.0);
if (datanode->GetFloatProperty("outline width", binaryOutlineWidth, renderer))
{
if (localStorage->m_Actors->GetNumberOfPaths() > 1)
{
float binaryOutlineShadowWidth(1.5);
datanode->GetFloatProperty("outline shadow width", binaryOutlineShadowWidth, renderer);
dynamic_cast<vtkActor *>(localStorage->m_Actors->GetParts()->GetItemAsObject(0))
->GetProperty()
->SetLineWidth(binaryOutlineWidth * binaryOutlineShadowWidth);
}
localStorage->m_Actor->GetProperty()->SetLineWidth(binaryOutlineWidth);
}
}
else
{
localStorage->m_ReslicedImage = nullptr;
localStorage->m_Mapper->SetInputData(localStorage->m_EmptyPolyData);
return;
}
this->ApplyOpacity(renderer);
this->ApplyRenderingMode(renderer);
// do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter)
localStorage->m_Texture->SetColorModeToDirectScalars();
int displayedComponent = 0;
if (datanode->GetIntProperty("Image.Displayed Component", displayedComponent, renderer) && numberOfComponents > 1)
{
localStorage->m_VectorComponentExtractor->SetComponents(displayedComponent);
localStorage->m_VectorComponentExtractor->SetInputData(localStorage->m_ReslicedImage);
localStorage->m_LevelWindowFilter->SetInputConnection(localStorage->m_VectorComponentExtractor->GetOutputPort(0));
}
else
{
// connect the input with the levelwindow filter
localStorage->m_LevelWindowFilter->SetInputData(localStorage->m_ReslicedImage);
}
// check for texture interpolation property
bool textureInterpolation = false;
GetDataNode()->GetBoolProperty("texture interpolation", textureInterpolation, renderer);
// set the interpolation modus according to the property
localStorage->m_Texture->SetInterpolate(textureInterpolation);
// connect the texture with the output of the levelwindow filter
localStorage->m_Texture->SetInputConnection(localStorage->m_LevelWindowFilter->GetOutputPort());
this->TransformActor(renderer);
vtkActor *contourShadowActor = dynamic_cast<vtkActor *>(localStorage->m_Actors->GetParts()->GetItemAsObject(0));
if (showIsoLines) // connect the mapper with the polyData which contains the lines
{
// We need the contour for the binary outline property as actor
localStorage->m_Mapper->SetInputData(localStorage->m_OutlinePolyData);
localStorage->m_Actor->SetTexture(nullptr); // no texture for contours
bool binaryOutlineShadow(false);
datanode->GetBoolProperty("outline binary shadow", binaryOutlineShadow, renderer);
if (binaryOutlineShadow)
contourShadowActor->SetVisibility(true);
else
contourShadowActor->SetVisibility(false);
}
else
{ // Connect the mapper with the input texture. This is the standard case.
// setup the textured plane
this->GeneratePlane(renderer, sliceBounds);
// set the plane as input for the mapper
localStorage->m_Mapper->SetInputConnection(localStorage->m_Plane->GetOutputPort());
// set the texture for the actor
localStorage->m_Actor->SetTexture(localStorage->m_Texture);
contourShadowActor->SetVisibility(false);
}
// We have been modified => save this for next Update()
localStorage->m_LastUpdateTime.Modified();
}
void mitk::DoseImageVtkMapper2D::ApplyLevelWindow(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = this->GetLocalStorage(renderer);
LevelWindow levelWindow;
this->GetDataNode()->GetLevelWindow(levelWindow, renderer, "levelwindow");
localStorage->m_LevelWindowFilter->GetLookupTable()->SetRange(levelWindow.GetLowerWindowBound(),
levelWindow.GetUpperWindowBound());
mitk::LevelWindow opacLevelWindow;
if (this->GetDataNode()->GetLevelWindow(opacLevelWindow, renderer, "opaclevelwindow"))
{
// pass the opaque level window to the filter
localStorage->m_LevelWindowFilter->SetMinOpacity(opacLevelWindow.GetLowerWindowBound());
localStorage->m_LevelWindowFilter->SetMaxOpacity(opacLevelWindow.GetUpperWindowBound());
}
else
{
// no opaque level window
localStorage->m_LevelWindowFilter->SetMinOpacity(0.0);
localStorage->m_LevelWindowFilter->SetMaxOpacity(255.0);
}
}
void mitk::DoseImageVtkMapper2D::ApplyColor(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = this->GetLocalStorage(renderer);
float rgb[3] = {1.0f, 1.0f, 1.0f};
// check for color prop and use it for rendering if it exists
// binary image hovering & binary image selection
bool hover = false;
bool selected = false;
GetDataNode()->GetBoolProperty("binaryimage.ishovering", hover, renderer);
GetDataNode()->GetBoolProperty("selected", selected, renderer);
if (hover && !selected)
{
mitk::ColorProperty::Pointer colorprop =
dynamic_cast<mitk::ColorProperty *>(GetDataNode()->GetProperty("binaryimage.hoveringcolor", renderer));
if (colorprop.IsNotNull())
{
memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float));
}
else
{
GetDataNode()->GetColor(rgb, renderer, "color");
}
}
if (selected)
{
mitk::ColorProperty::Pointer colorprop =
dynamic_cast<mitk::ColorProperty *>(GetDataNode()->GetProperty("binaryimage.selectedcolor", renderer));
if (colorprop.IsNotNull())
{
memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float));
}
else
{
GetDataNode()->GetColor(rgb, renderer, "color");
}
}
if (!hover && !selected)
{
GetDataNode()->GetColor(rgb, renderer, "color");
}
double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; // conversion to double for VTK
dynamic_cast<vtkActor *>(localStorage->m_Actors->GetParts()->GetItemAsObject(0))->GetProperty()->SetColor(rgbConv);
localStorage->m_Actor->GetProperty()->SetColor(rgbConv);
if (localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1)
{
float rgb[3] = {1.0f, 1.0f, 1.0f};
mitk::ColorProperty::Pointer colorprop =
dynamic_cast<mitk::ColorProperty *>(GetDataNode()->GetProperty("outline binary shadow color", renderer));
if (colorprop.IsNotNull())
{
memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float));
}
double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; // conversion to double for VTK
dynamic_cast<vtkActor *>(localStorage->m_Actors->GetParts()->GetItemAsObject(0))->GetProperty()->SetColor(rgbConv);
}
}
void mitk::DoseImageVtkMapper2D::ApplyOpacity(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = this->GetLocalStorage(renderer);
float opacity = 1.0f;
// check for opacity prop and use it for rendering if it exists
GetDataNode()->GetOpacity(opacity, renderer, "opacity");
// set the opacity according to the properties
localStorage->m_Actor->GetProperty()->SetOpacity(opacity);
if (localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1)
{
dynamic_cast<vtkActor *>(localStorage->m_Actors->GetParts()->GetItemAsObject(0))
->GetProperty()
->SetOpacity(opacity);
}
}
void mitk::DoseImageVtkMapper2D::ApplyRenderingMode(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
bool binary = false;
this->GetDataNode()->GetBoolProperty("binary", binary, renderer);
if (binary) // is it a binary image?
{
// for binary images, we always use our default LuT and map every value to (0,1)
// the opacity of 0 will always be 0.0. We never a apply a LuT/TfF nor a level window.
localStorage->m_LevelWindowFilter->SetLookupTable(localStorage->m_BinaryLookupTable);
}
else
{
// all other image types can make use of the rendering mode
int renderingMode = mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR;
mitk::RenderingModeProperty::Pointer mode =
dynamic_cast<mitk::RenderingModeProperty *>(this->GetDataNode()->GetProperty("Image Rendering.Mode", renderer));
if (mode.IsNotNull())
{
renderingMode = mode->GetRenderingMode();
}
switch (renderingMode)
{
case mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR:
MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_LookupTable_Color";
this->ApplyLookuptable(renderer);
this->ApplyLevelWindow(renderer);
break;
case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR:
MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_ColorTransferFunction_Color";
this->ApplyColorTransferFunction(renderer);
this->ApplyLevelWindow(renderer);
break;
case mitk::RenderingModeProperty::LOOKUPTABLE_COLOR:
MITK_DEBUG << "'Image Rendering.Mode' = LookupTable_Color";
this->ApplyLookuptable(renderer);
break;
case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR:
MITK_DEBUG << "'Image Rendering.Mode' = ColorTransferFunction_Color";
this->ApplyColorTransferFunction(renderer);
break;
default:
MITK_ERROR << "No valid 'Image Rendering.Mode' set. Using LOOKUPTABLE_LEVELWINDOW_COLOR instead.";
this->ApplyLookuptable(renderer);
this->ApplyLevelWindow(renderer);
break;
}
}
// we apply color for all images (including binaries).
this->ApplyColor(renderer);
}
void mitk::DoseImageVtkMapper2D::ApplyLookuptable(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
vtkLookupTable *usedLookupTable = localStorage->m_ColorLookupTable;
// If lookup table or transferfunction use is requested...
mitk::LookupTableProperty::Pointer lookupTableProp =
dynamic_cast<mitk::LookupTableProperty *>(this->GetDataNode()->GetProperty("LookupTable"));
if (lookupTableProp.IsNotNull()) // is a lookuptable set?
{
usedLookupTable = lookupTableProp->GetLookupTable()->GetVtkLookupTable();
}
else
{
//"Image Rendering.Mode was set to use a lookup table but there is no property 'LookupTable'.
// A default (rainbow) lookup table will be used.
// Here have to do nothing. Warning for the user has been removed, due to unwanted console output
// in every interation of the rendering.
}
localStorage->m_LevelWindowFilter->SetLookupTable(usedLookupTable);
}
void mitk::DoseImageVtkMapper2D::ApplyColorTransferFunction(mitk::BaseRenderer *renderer)
{
mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast<mitk::TransferFunctionProperty *>(
this->GetDataNode()->GetProperty("Image Rendering.Transfer Function", renderer));
if (transferFunctionProp.IsNull())
{
MITK_ERROR << "'Image Rendering.Mode'' was set to use a color transfer function but there is no property 'Image "
"Rendering.Transfer Function'. Nothing will be done.";
return;
}
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
// pass the transfer function to our level window filter
localStorage->m_LevelWindowFilter->SetLookupTable(transferFunctionProp->GetValue()->GetColorTransferFunction());
}
void mitk::DoseImageVtkMapper2D::Update(mitk::BaseRenderer *renderer)
{
bool visible = true;
GetDataNode()->GetVisibility(visible, renderer, "visible");
if (!visible)
{
return;
}
mitk::Image *data = const_cast<mitk::Image *>(this->GetInput());
if (data == nullptr)
{
return;
}
// Calculate time step of the input data for the specified renderer (integer value)
this->CalculateTimeStep(renderer);
// Check if time step is valid
const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry();
if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) ||
(!dataTimeGeometry->IsValidTimeStep(this->GetTimestep())))
{
return;
}
const DataNode *node = this->GetDataNode();
data->UpdateOutputInformation();
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
// check if something important has changed and we need to rerender
if ((localStorage->m_LastUpdateTime < node->GetMTime()) // was the node modified?
||
(localStorage->m_LastUpdateTime < data->GetPipelineMTime()) // Was the data modified?
||
(localStorage->m_LastUpdateTime <
renderer->GetCurrentWorldPlaneGeometryUpdateTime()) // was the geometry modified?
||
(localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) ||
(localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) // was a property modified?
||
(localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime()))
{
this->GenerateDataForRenderer(renderer);
}
// since we have checked that nothing important has changed, we can set
// m_LastUpdateTime to the current time
localStorage->m_LastUpdateTime.Modified();
}
void mitk::DoseImageVtkMapper2D::SetDefaultProperties(mitk::DataNode *node,
mitk::BaseRenderer *renderer,
bool overwrite)
{
mitk::Image::Pointer image = dynamic_cast<mitk::Image *>(node->GetData());
// Properties common for both images and segmentations
node->AddProperty("depthOffset", mitk::FloatProperty::New(0.0), renderer, overwrite);
node->AddProperty("outline binary", mitk::BoolProperty::New(false), renderer, overwrite);
node->AddProperty("outline width", mitk::FloatProperty::New(1.0), renderer, overwrite);
node->AddProperty("outline binary shadow", mitk::BoolProperty::New(false), renderer, overwrite);
node->AddProperty("outline binary shadow color", ColorProperty::New(0.0, 0.0, 0.0), renderer, overwrite);
node->AddProperty("outline shadow width", mitk::FloatProperty::New(1.5), renderer, overwrite);
if (image->IsRotated())
node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_CUBIC));
else
node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New());
node->AddProperty("texture interpolation",
mitk::BoolProperty::New(false)); // default value
node->AddProperty("in plane resample extent by geometry", mitk::BoolProperty::New(false));
node->AddProperty("bounding box", mitk::BoolProperty::New(false));
mitk::RenderingModeProperty::Pointer renderingModeProperty = mitk::RenderingModeProperty::New();
node->AddProperty("Image Rendering.Mode", renderingModeProperty);
// Set default grayscale look-up table
mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New();
mitkLut->SetType(mitk::LookupTable::GRAYSCALE);
mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New();
mitkLutProp->SetLookupTable(mitkLut);
node->SetProperty("LookupTable", mitkLutProp);
std::string photometricInterpretation; // DICOM tag telling us how pixel values should be displayed
if (node->GetStringProperty("dicom.pixel.PhotometricInterpretation", photometricInterpretation))
{
// modality provided by DICOM or other reader
if (photometricInterpretation.find("MONOCHROME1") != std::string::npos) // meaning: display MINIMUM pixels as WHITE
{
// Set inverse grayscale look-up table
mitkLut->SetType(mitk::LookupTable::INVERSE_GRAYSCALE);
mitkLutProp->SetLookupTable(mitkLut);
node->SetProperty("LookupTable", mitkLutProp);
}
// Otherwise do nothing - the default grayscale look-up table has already been set
}
bool isBinaryImage(false);
if (!node->GetBoolProperty("binary", isBinaryImage))
{
// ok, property is not set, use heuristic to determine if this
// is a binary image
mitk::Image::Pointer centralSliceImage;
ScalarType minValue = 0.0;
ScalarType maxValue = 0.0;
ScalarType min2ndValue = 0.0;
ScalarType max2ndValue = 0.0;
mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New();
sliceSelector->SetInput(image);
sliceSelector->SetSliceNr(image->GetDimension(2) / 2);
sliceSelector->SetTimeNr(image->GetDimension(3) / 2);
sliceSelector->SetChannelNr(image->GetDimension(4) / 2);
sliceSelector->Update();
centralSliceImage = sliceSelector->GetOutput();
if (centralSliceImage.IsNotNull() && centralSliceImage->IsInitialized())
{
minValue = centralSliceImage->GetStatistics()->GetScalarValueMin();
maxValue = centralSliceImage->GetStatistics()->GetScalarValueMax();
min2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMin();
max2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMax();
}
if ((maxValue == min2ndValue && minValue == max2ndValue) || minValue == maxValue)
{
// centralSlice is strange, lets look at all data
minValue = image->GetStatistics()->GetScalarValueMin();
maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute();
min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute();
max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute();
}
isBinaryImage = (maxValue == min2ndValue && minValue == max2ndValue);
}
// some more properties specific for a binary...
if (isBinaryImage)
{
node->AddProperty("opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite);
node->AddProperty("color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite);
node->AddProperty("binaryimage.selectedcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite);
node->AddProperty("binaryimage.selectedannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite);
node->AddProperty("binaryimage.hoveringcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite);
node->AddProperty("binaryimage.hoveringannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite);
node->AddProperty("binary", mitk::BoolProperty::New(true), renderer, overwrite);
node->AddProperty("layer", mitk::IntProperty::New(10), renderer, overwrite);
}
else //...or image type object
{
node->AddProperty("opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite);
node->AddProperty("color", ColorProperty::New(1.0, 1.0, 1.0), renderer, overwrite);
node->AddProperty("binary", mitk::BoolProperty::New(false), renderer, overwrite);
node->AddProperty("layer", mitk::IntProperty::New(0), renderer, overwrite);
std::string className = image->GetNameOfClass();
if (className != "TensorImage" && className != "OdfImage" && className != "ShImage")
{
PixelType pixelType = image->GetPixelType();
size_t numComponents = pixelType.GetNumberOfComponents();
if ((pixelType.GetPixelTypeAsString() == "vector" && numComponents > 1) || numComponents == 2 ||
numComponents > 4)
node->AddProperty("Image.Displayed Component", mitk::IntProperty::New(0), renderer, overwrite);
}
}
if (image.IsNotNull() && image->IsInitialized())
{
if ((overwrite) || (node->GetProperty("levelwindow", renderer) == nullptr))
{
/* initialize level/window from DICOM tags */
std::string sLevel;
std::string sWindow;
if (GetBackwardsCompatibleDICOMProperty(
0x0028, 0x1050, "dicom.voilut.WindowCenter", image->GetPropertyList(), sLevel) &&
GetBackwardsCompatibleDICOMProperty(
0x0028, 0x1051, "dicom.voilut.WindowWidth", image->GetPropertyList(), sWindow))
{
float level = atof(sLevel.c_str());
float window = atof(sWindow.c_str());
mitk::LevelWindow contrast;
std::string sSmallestPixelValueInSeries;
std::string sLargestPixelValueInSeries;
if (GetBackwardsCompatibleDICOMProperty(0x0028,
0x0108,
"dicom.series.SmallestPixelValueInSeries",
image->GetPropertyList(),
sSmallestPixelValueInSeries) &&
GetBackwardsCompatibleDICOMProperty(0x0028,
0x0109,
"dicom.series.LargestPixelValueInSeries",
image->GetPropertyList(),
sLargestPixelValueInSeries))
{
float smallestPixelValueInSeries = atof(sSmallestPixelValueInSeries.c_str());
float largestPixelValueInSeries = atof(sLargestPixelValueInSeries.c_str());
contrast.SetRangeMinMax(smallestPixelValueInSeries - 1,
largestPixelValueInSeries + 1); // why not a little buffer?
// might remedy some l/w widget challenges
}
else
{
contrast.SetAuto(static_cast<mitk::Image *>(node->GetData()), false, true); // we need this as a fallback
}
contrast.SetLevelWindow(level, window, true);
node->SetProperty("levelwindow", LevelWindowProperty::New(contrast), renderer);
}
}
if (((overwrite) || (node->GetProperty("opaclevelwindow", renderer) == nullptr)) &&
- (image->GetPixelType().GetPixelType() == itk::ImageIOBase::RGBA) &&
- (image->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR))
+ (image->GetPixelType().GetPixelType() == itk::IOPixelEnum::RGBA) &&
+ (image->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR))
{
mitk::LevelWindow opaclevwin;
opaclevwin.SetRangeMinMax(0, 255);
opaclevwin.SetWindowBounds(0, 255);
mitk::LevelWindowProperty::Pointer prop = mitk::LevelWindowProperty::New(opaclevwin);
node->SetProperty("opaclevelwindow", prop, renderer);
}
}
Superclass::SetDefaultProperties(node, renderer, overwrite);
}
mitk::DoseImageVtkMapper2D::LocalStorage *mitk::DoseImageVtkMapper2D::GetLocalStorage(mitk::BaseRenderer *renderer)
{
return m_LSH.GetLocalStorage(renderer);
}
vtkSmartPointer<vtkPolyData> mitk::DoseImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer *renderer)
{
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New(); // the points to draw
vtkSmartPointer<vtkCellArray> lines = vtkSmartPointer<vtkCellArray>::New(); // the lines to connect the points
vtkSmartPointer<vtkUnsignedCharArray> colors = vtkSmartPointer<vtkUnsignedCharArray>::New();
colors->SetNumberOfComponents(3);
colors->SetName("Colors");
float pref;
this->GetDataNode()->GetFloatProperty(mitk::RTConstants::REFERENCE_DOSE_PROPERTY_NAME.c_str(), pref);
mitk::IsoDoseLevelSetProperty::Pointer propIsoSet = dynamic_cast<mitk::IsoDoseLevelSetProperty *>(
GetDataNode()->GetProperty(mitk::RTConstants::DOSE_ISO_LEVELS_PROPERTY_NAME.c_str()));
mitk::IsoDoseLevelSet::Pointer isoDoseLevelSet = propIsoSet->GetValue();
for (mitk::IsoDoseLevelSet::ConstIterator doseIT = isoDoseLevelSet->Begin(); doseIT != isoDoseLevelSet->End();
++doseIT)
{
if (doseIT->GetVisibleIsoLine())
{
this->CreateLevelOutline(renderer, &(doseIT.Value()), pref, points, lines, colors);
} // end of if visible dose value
} // end of loop over all does values
mitk::IsoDoseLevelVectorProperty::Pointer propfreeIsoVec = dynamic_cast<mitk::IsoDoseLevelVectorProperty *>(
GetDataNode()->GetProperty(mitk::RTConstants::DOSE_FREE_ISO_VALUES_PROPERTY_NAME.c_str()));
mitk::IsoDoseLevelVector::Pointer frereIsoDoseLevelVec = propfreeIsoVec->GetValue();
for (mitk::IsoDoseLevelVector::ConstIterator freeDoseIT = frereIsoDoseLevelVec->Begin();
freeDoseIT != frereIsoDoseLevelVec->End();
++freeDoseIT)
{
if (freeDoseIT->Value()->GetVisibleIsoLine())
{
this->CreateLevelOutline(renderer, freeDoseIT->Value(), pref, points, lines, colors);
} // end of if visible dose value
} // end of loop over all does values
// Create a polydata to store everything in
vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New();
// Add the points to the dataset
polyData->SetPoints(points);
// Add the lines to the dataset
polyData->SetLines(lines);
polyData->GetCellData()->SetScalars(colors);
return polyData;
}
void mitk::DoseImageVtkMapper2D::CreateLevelOutline(mitk::BaseRenderer *renderer,
const mitk::IsoDoseLevel *level,
float pref,
vtkSmartPointer<vtkPoints> points,
vtkSmartPointer<vtkCellArray> lines,
vtkSmartPointer<vtkUnsignedCharArray> colors)
{
LocalStorage *localStorage = this->GetLocalStorage(renderer);
// get the min and max index values of each direction
int *extent = localStorage->m_ReslicedImage->GetExtent();
int xMin = extent[0];
int xMax = extent[1];
int yMin = extent[2];
int yMax = extent[3];
int *dims = localStorage->m_ReslicedImage->GetDimensions(); // dimensions of the image
int line = dims[0]; // how many pixels per line?
// get the depth for each contour
float depth = CalculateLayerDepth(renderer);
double doseValue = level->GetDoseValue() * pref;
mitk::IsoDoseLevel::ColorType isoColor = level->GetColor();
unsigned char colorLine[3] = {static_cast<unsigned char>(isoColor.GetRed() * 255),
static_cast<unsigned char>(isoColor.GetGreen() * 255),
static_cast<unsigned char>(isoColor.GetBlue() * 255)};
int x = xMin; // pixel index x
int y = yMin; // pixel index y
float *currentPixel;
// We take the pointer to the first pixel of the image
currentPixel = static_cast<float *>(localStorage->m_ReslicedImage->GetScalarPointer());
if (!currentPixel){
mitkThrow() << "currentPixel invalid";
}
while (y <= yMax)
{
// if the current pixel value is set to something
if ((currentPixel) && (*currentPixel >= doseValue))
{
// check in which direction a line is necessary
// a line is added if the neighbor of the current pixel has the value 0
// and if the pixel is located at the edge of the image
// if vvvvv not the first line vvvvv
if (y > yMin && *(currentPixel - line) < doseValue)
{ // x direction - bottom edge of the pixel
// add the 2 points
vtkIdType p1 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 =
points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
// add the line between both points
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
colors->InsertNextTypedTuple(colorLine);
}
// if vvvvv not the last line vvvvv
if (y < yMax && *(currentPixel + line) < doseValue)
{ // x direction - top edge of the pixel
vtkIdType p1 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 = points->InsertNextPoint(
(x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
colors->InsertNextTypedTuple(colorLine);
}
// if vvvvv not the first pixel vvvvv
if ((x > xMin || y > yMin) && *(currentPixel - 1) < doseValue)
{ // y direction - left edge of the pixel
vtkIdType p1 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
colors->InsertNextTypedTuple(colorLine);
}
// if vvvvv not the last pixel vvvvv
if ((y < yMax || (x < xMax)) && *(currentPixel + 1) < doseValue)
{ // y direction - right edge of the pixel
vtkIdType p1 =
points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 = points->InsertNextPoint(
(x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
colors->InsertNextTypedTuple(colorLine);
}
/* now consider pixels at the edge of the image */
// if vvvvv left edge of image vvvvv
if (x == xMin)
{ // draw left edge of the pixel
vtkIdType p1 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
colors->InsertNextTypedTuple(colorLine);
}
// if vvvvv right edge of image vvvvv
if (x == xMax)
{ // draw right edge of the pixel
vtkIdType p1 =
points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 = points->InsertNextPoint(
(x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
colors->InsertNextTypedTuple(colorLine);
}
// if vvvvv bottom edge of image vvvvv
if (y == yMin)
{ // draw bottom edge of the pixel
vtkIdType p1 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 =
points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
colors->InsertNextTypedTuple(colorLine);
}
// if vvvvv top edge of image vvvvv
if (y == yMax)
{ // draw top edge of the pixel
vtkIdType p1 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 = points->InsertNextPoint(
(x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
colors->InsertNextTypedTuple(colorLine);
}
} // end if currentpixel is set
x++;
if (x > xMax)
{ // reached end of line
x = xMin;
y++;
}
// Increase the pointer-position to the next pixel.
// This is safe, as the while-loop and the x-reset logic above makes
// sure we do not exceed the bounds of the image
currentPixel++;
} // end of while
}
void mitk::DoseImageVtkMapper2D::TransformActor(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
// get the transformation matrix of the reslicer in order to render the slice as axial, coronal or saggital
vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
vtkSmartPointer<vtkMatrix4x4> matrix = localStorage->m_Reslicer->GetResliceAxes();
trans->SetMatrix(matrix);
// transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or saggital)
localStorage->m_Actor->SetUserTransform(trans);
// transform the origin to center based coordinates, because MITK is center based.
localStorage->m_Actor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0);
if (localStorage->m_Actors->GetNumberOfPaths() > 1)
{
vtkActor *secondaryActor = dynamic_cast<vtkActor *>(localStorage->m_Actors->GetParts()->GetItemAsObject(0));
secondaryActor->SetUserTransform(trans);
secondaryActor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0);
}
}
bool mitk::DoseImageVtkMapper2D::RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry,
SlicedGeometry3D *imageGeometry)
{
// if either one of the two geometries is nullptr we return true
// for safety reasons
if (renderingGeometry == nullptr || imageGeometry == nullptr)
return true;
// get the distance for the first cornerpoint
ScalarType initialDistance = renderingGeometry->SignedDistance(imageGeometry->GetCornerPoint(0));
for (int i = 1; i < 8; i++)
{
mitk::Point3D cornerPoint = imageGeometry->GetCornerPoint(i);
// get the distance to the other cornerpoints
ScalarType distance = renderingGeometry->SignedDistance(cornerPoint);
// if it has not the same signing as the distance of the first point
if (initialDistance * distance < 0)
{
// we have an intersection and return true
return true;
}
}
// all distances have the same sign, no intersection and we return false
return false;
}
mitk::DoseImageVtkMapper2D::LocalStorage::~LocalStorage()
{
}
mitk::DoseImageVtkMapper2D::LocalStorage::LocalStorage()
: m_VectorComponentExtractor(vtkSmartPointer<vtkImageExtractComponents>::New())
{
m_LevelWindowFilter = vtkSmartPointer<vtkMitkLevelWindowFilter>::New();
// Do as much actions as possible in here to avoid double executions.
m_Plane = vtkSmartPointer<vtkPlaneSource>::New();
m_Texture = vtkSmartPointer<vtkNeverTranslucentTexture>::New().GetPointer();
m_DefaultLookupTable = vtkSmartPointer<vtkLookupTable>::New();
m_BinaryLookupTable = vtkSmartPointer<vtkLookupTable>::New();
m_ColorLookupTable = vtkSmartPointer<vtkLookupTable>::New();
m_Mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
m_Actor = vtkSmartPointer<vtkActor>::New();
m_Actors = vtkSmartPointer<vtkPropAssembly>::New();
m_Reslicer = mitk::ExtractSliceFilter::New();
m_TSFilter = vtkSmartPointer<vtkMitkThickSlicesFilter>::New();
m_OutlinePolyData = vtkSmartPointer<vtkPolyData>::New();
m_ReslicedImage = vtkSmartPointer<vtkImageData>::New();
m_EmptyPolyData = vtkSmartPointer<vtkPolyData>::New();
// the following actions are always the same and thus can be performed
// in the constructor for each image (i.e. the image-corresponding local storage)
m_TSFilter->ReleaseDataFlagOn();
mitk::LookupTable::Pointer mitkLUT = mitk::LookupTable::New();
// built a default lookuptable
mitkLUT->SetType(mitk::LookupTable::GRAYSCALE);
m_DefaultLookupTable = mitkLUT->GetVtkLookupTable();
mitkLUT->SetType(mitk::LookupTable::LEGACY_BINARY);
m_BinaryLookupTable = mitkLUT->GetVtkLookupTable();
mitkLUT->SetType(mitk::LookupTable::LEGACY_RAINBOW_COLOR);
m_ColorLookupTable = mitkLUT->GetVtkLookupTable();
// do not repeat the texture (the image)
m_Texture->RepeatOff();
// set the mapper for the actor
m_Actor->SetMapper(m_Mapper);
vtkSmartPointer<vtkActor> outlineShadowActor = vtkSmartPointer<vtkActor>::New();
outlineShadowActor->SetMapper(m_Mapper);
m_Actors->AddPart(outlineShadowActor);
m_Actors->AddPart(m_Actor);
}
diff --git a/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.h b/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.h
index a8580e38c1..fdcb418dca 100644
--- a/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.h
+++ b/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.h
@@ -1,250 +1,250 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef __itkAdaptiveThresholdIterator_h
#define __itkAdaptiveThresholdIterator_h
#include "itkConditionalConstIterator.h"
#include "itkImage.h"
#include "itkIndex.h"
#include "itkSize.h"
#include <map>
#include <queue>
#include <utility>
#include <vector>
namespace itk
{
/**
* \class AdaptiveThresholdIterator
* \brief Iterates over an image using a variable image function,
* which threshold can be varied during the iteration process.
*
* \ingroup ImageIterators
*
*/
template <class TImage, class TFunction>
class ITK_EXPORT AdaptiveThresholdIterator : public ConditionalConstIterator<TImage>
{
public:
/** Standard class typedefs. */
typedef AdaptiveThresholdIterator Self;
typedef ConditionalConstIterator<TImage> Superclass;
typedef TImage ImageType;
// A temporary image used for storing info about all indices
typedef TImage TTempImage;
typename TTempImage::Pointer tempPtr;
//[!] isn't really used?!
/** Type of function */
typedef TFunction FunctionType;
/** Type of vector used to store location info in the spatial function */
typedef typename TFunction::InputType FunctionInputType;
/** Size typedef support. */
typedef typename TImage::SizeType SizeType;
/** Region typedef support */
typedef typename TImage::RegionType RegionType;
typedef typename TImage::IndexType IndexType;
/** Internal Pixel Type */
typedef typename TImage::InternalPixelType InternalPixelType;
/** External Pixel Type */
typedef typename TImage::PixelType PixelType;
/** Queue containing indices representing a voxel position */
typedef std::queue<IndexType> IndexQueueType;
/** Map used to generate the output result */
typedef std::map<unsigned int, IndexQueueType> QueueMapType;
/** Dimension of the image the iterator walks. This constant is needed so
* that functions that are templated over image iterator type (as opposed to
* being templated over pixel type and dimension) can have compile time
* access to the dimension of the image that the iterator walks. */
itkStaticConstMacro(NDimensions, unsigned int, TImage::ImageDimension);
/** Constructor establishes an iterator to walk a particular image and a
* particular region of that image. This version of the constructor uses
* an explicit seed pixel for the flood fill, the "startIndex" */
AdaptiveThresholdIterator(ImageType *imagePtr, FunctionType *fnPtr, IndexType startIndex);
/** Constructor establishes an iterator to walk a particular image and a
* particular region of that image. This version of the constructor uses
* an explicit list of seed pixels for the flood fill, the "startIndex" */
AdaptiveThresholdIterator(ImageType *imagePtr, FunctionType *fnPtr, std::vector<IndexType> &startIndex);
/** Constructor establishes an iterator to walk a particular image and a
* particular region of that image. This version of the constructor
* should be used when the seed pixel is unknown */
AdaptiveThresholdIterator(ImageType *imagePtr, FunctionType *fnPtr);
/** Default Destructor. */
~AdaptiveThresholdIterator() override{};
/** Initializes the iterator, called from constructor */
void InitializeIterator();
// makes the iterator go one step further
void DoExtendedFloodStep();
// set-method for member-variable
void SetExpansionDirection(bool upwards);
// Init-method
void InitRegionGrowingState();
void SetMinTH(int min);
void SetMaxTH(int max);
int GetSeedPointValue(void);
/** switch between fine and raw leakage detection */
void SetFineDetectionMode(bool fine = false)
{
m_FineDetectionMode = fine;
m_DetectionStop = false;
}
/** Get the index. This provides a read only reference to the index.
* This causes the index to be calculated from pointer arithmetic and is
* therefore an expensive operation.
* \sa SetIndex */
const IndexType GetIndex() override
{
return (*m_QueueMap.find(m_RegionGrowingState)).second.front();
} // [!] is never called?!
const PixelType Get(void) const override
{
return const_cast<ImageType *>(this->m_Image.GetPointer())
->GetPixel((*m_QueueMap.find(m_RegionGrowingState)).second.front());
}
//[!] is never called?!
void Set(const PixelType &value)
{
const_cast<ImageType *>(this->m_Image.GetPointer())
->GetPixel((*m_QueueMap.find(m_RegionGrowingState)).second.front()) = value;
}
void GoToBegin();
/** Is the iterator at the end of the region? */
- bool IsAtEnd() override { return this->m_IsAtEnd; };
+ bool IsAtEnd() const override { return this->m_IsAtEnd; };
/** Walk forward one index */
void operator++() override { this->DoExtendedFloodStep(); }
virtual SmartPointer<FunctionType> GetFunction() const { return m_Function; }
/** operator= is provided to make sure the handle to the image is properly
* reference counted. */
Self &operator=(const Self &it)
{
this->m_Image = it.m_Image; // copy the smart pointer
this->m_Region = it.m_Region;
this->m_InitializeValue = it.m_InitializeValue;
this->m_RegionGrowingState = it.m_RegionGrowingState;
this->m_MinTH = it.m_MinTH;
this->m_MaxTH = it.m_MaxTH;
this->m_SeedPointValue = it.m_SeedPointValue;
this->m_VoxelCounter = it.m_VoxelCounter;
this->m_LastVoxelNumber = it.m_LastVoxelNumber;
this->m_DetectedLeakagePoint = it.m_DetectedLeakagePoint;
this->m_CurrentLeakageRatio = it.m_CurrentLeakageRatio;
return *this;
}
/** Compute whether the index of interest should be included in the flood */
bool IsPixelIncluded(const IndexType &index) const override;
// Calculate the value the outputImage is initialized to
static int CalculateInitializeValue(int lower, int upper) { return ((upper - lower) + 1) * (-1); };
int GetLeakagePoint(void) { return m_DetectedLeakagePoint; }
protected:
/*
* @brief Pointer on the output image to which the result shall be written
*/
SmartPointer<ImageType> m_OutputImage;
SmartPointer<FunctionType> m_Function;
/** A list of locations to start the recursive fill */
std::vector<IndexType> m_StartIndices;
/** The origin of the source image */
typename ImageType::PointType m_ImageOrigin;
/** The spacing of the source image */
typename ImageType::SpacingType m_ImageSpacing;
/** Region of the source image */
RegionType m_ImageRegion;
bool m_UpwardsExpansion;
int m_InitializeValue;
void ExpandThresholdUpwards();
void ExpandThresholdDownwards();
void IncrementRegionGrowingState();
// calculates how many steps the voxel is from the current step
int EstimateDistance(IndexType);
// calculates how many expansion steps will be taken
unsigned int CalculateMaxRGS();
private:
void InsertIndexTypeIntoQueueMap(unsigned int key, IndexType index);
int m_RegionGrowingState;
QueueMapType m_QueueMap;
int m_MinTH;
int m_MaxTH;
int m_SeedPointValue;
unsigned int m_VoxelCounter;
unsigned int m_LastVoxelNumber;
int m_DetectedLeakagePoint;
float m_CurrentLeakageRatio;
void CheckSeedPointValue();
/* flag for switching between raw leakage detection (bigger bronchial vessels)
* and fine leakage detection (smaller bronchial vessels [starting from leaves])
*/
bool m_FineDetectionMode;
bool m_DetectionStop;
};
} // end namespace itk
#ifndef ITK_MANUAL_INSTANTIATION
#include "itkAdaptiveThresholdIterator.txx"
#endif
#endif
diff --git a/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx b/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx
index cf9855e799..aeb755b305 100644
--- a/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx
+++ b/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx
@@ -1,422 +1,422 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef _itkAdaptiveThresholdIterator_txx
#define _itkAdaptiveThresholdIterator_txx
#include "itkAdaptiveThresholdIterator.h"
#include "mitkProgressBar.h"
#include <cmath>
namespace itk
{
template <class TImage, class TFunction>
AdaptiveThresholdIterator<TImage, TFunction>::AdaptiveThresholdIterator(ImageType *imagePtr,
FunctionType *fnPtr,
IndexType startIndex)
: m_FineDetectionMode(false), m_DetectionStop(false)
{
this->m_OutputImage = imagePtr;
m_Function = fnPtr;
m_StartIndices.push_back(startIndex);
// Set up the temporary image
this->InitializeIterator();
}
template <class TImage, class TFunction>
AdaptiveThresholdIterator<TImage, TFunction>::AdaptiveThresholdIterator(ImageType *imagePtr,
FunctionType *fnPtr,
std::vector<IndexType> &startIndex)
: m_FineDetectionMode(false), m_DetectionStop(false)
{
this->m_OutputImage = imagePtr;
m_Function = fnPtr;
unsigned int i;
for (i = 0; i < startIndex.size(); i++)
{
m_StartIndices.push_back(startIndex[i]);
}
// Set up the temporary image
this->InitializeIterator();
}
template <class TImage, class TFunction>
AdaptiveThresholdIterator<TImage, TFunction>::AdaptiveThresholdIterator(ImageType *imagePtr, FunctionType *fnPtr)
: m_FineDetectionMode(false), m_DetectionStop(false)
{
this->m_OutputImage = imagePtr; // here we store the image, we have to wite the result to
m_Function = fnPtr;
// Set up the temporary image
this->InitializeIterator();
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::InitializeIterator()
{
// Get the origin and spacing from the image in simple arrays
m_ImageOrigin = this->m_OutputImage->GetOrigin();
m_ImageSpacing = this->m_OutputImage->GetSpacing();
m_ImageRegion = this->m_OutputImage->GetBufferedRegion();
this->InitRegionGrowingState();
m_VoxelCounter = 0;
m_LastVoxelNumber = 0;
m_CurrentLeakageRatio = 0;
m_DetectedLeakagePoint = 0;
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::SetExpansionDirection(bool upwards)
{
m_UpwardsExpansion = upwards;
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::IncrementRegionGrowingState()
{
// make the progressbar go one step further
if (!m_FineDetectionMode)
mitk::ProgressBar::GetInstance()->Progress();
// updating the thresholds
if (m_UpwardsExpansion)
{
this->ExpandThresholdUpwards();
}
else
{
this->ExpandThresholdDownwards();
}
// leakage-detection
int criticalValue = 2000; // calculate a bit more "intelligent"
if (!m_FineDetectionMode)
{
int diff = m_VoxelCounter - m_LastVoxelNumber;
if (diff > m_CurrentLeakageRatio)
{
m_CurrentLeakageRatio = diff;
m_DetectedLeakagePoint = m_RegionGrowingState;
}
m_LastVoxelNumber = m_VoxelCounter;
m_VoxelCounter = 0;
}
else // fine leakage detection
{
// counting voxels over interations; if above a critical value (to be extended) then set this to leakage
int diff = m_VoxelCounter - m_LastVoxelNumber;
// std::cout<<"diff is: "<<diff<<"\n";
if (diff <= criticalValue && (!m_DetectionStop))
{
// m_CurrentLeakageRatio = diff;
m_DetectedLeakagePoint = m_RegionGrowingState + 1; // TODO check why this is needed
// std::cout<<"setting CurrentLeakageRatio to: "<<diff<<" and leakagePoint to: "<<m_DetectedLeakagePoint<<"\n";
}
else
{
m_DetectionStop = true;
// std::cout<<"\n\n[ITERATOR] detection stop!!!\n";
}
m_LastVoxelNumber = m_VoxelCounter;
m_VoxelCounter = 0;
}
// increment the counter
m_RegionGrowingState++;
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::ExpandThresholdUpwards()
{
int upper = (int)m_Function->GetUpper();
upper++;
m_Function->ThresholdBetween(m_MinTH, upper);
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::ExpandThresholdDownwards()
{
int lower = (int)m_Function->GetLower();
lower--;
m_Function->ThresholdBetween(lower, m_MaxTH);
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::InitRegionGrowingState()
{
this->m_RegionGrowingState = 1;
}
template <class TImage, class TFunction>
int AdaptiveThresholdIterator<TImage, TFunction>::EstimateDistance(IndexType tempIndex)
{
PixelType value = this->m_Function->GetInputImage()->GetPixel(tempIndex);
PixelType minPixel = PixelType(m_MinTH);
PixelType maxPixel = PixelType(m_MaxTH);
if (value > maxPixel || value < minPixel)
{
return 0;
}
if (m_UpwardsExpansion)
{
return (int)(value - m_SeedPointValue);
}
else
{
return (int)(m_SeedPointValue - value);
}
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::SetMinTH(int min)
{
m_MinTH = min;
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::SetMaxTH(int max)
{
m_MaxTH = max;
}
template <class TImage, class TFunction>
int AdaptiveThresholdIterator<TImage, TFunction>::GetSeedPointValue()
{
return this->m_SeedPointValue;
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::GoToBegin()
{
m_QueueMap.clear();
m_SeedPointValue = 0;
IndexType seedIndex = m_StartIndices[0];
bool doAverage = false; // enable or disable manually!
if (doAverage)
{
// loops for creating the sum of the N27-neighborhood around the seedpoint
for (int i = -1; i <= 1; i++)
{
for (int j = -1; j <= 1; j++)
{
for (int k = -1; k <= 1; k++)
{
seedIndex[0] = seedIndex[0] + i;
seedIndex[1] = seedIndex[1] + j;
seedIndex[2] = seedIndex[2] + k;
m_SeedPointValue += (int)m_Function->GetInputImage()->GetPixel(seedIndex);
}
}
}
// value of seedpoint computed from mean of N27-neighborhood
m_SeedPointValue = m_SeedPointValue / 27;
}
else
{
m_SeedPointValue = (int)m_Function->GetInputImage()->GetPixel(seedIndex);
}
this->CheckSeedPointValue();
m_InitializeValue = (this->CalculateMaxRGS() + 1);
if (!m_FineDetectionMode)
mitk::ProgressBar::GetInstance()->AddStepsToDo(m_InitializeValue - 1);
// only initialize with zeros for the first segmention (raw segmentation mode)
if (!m_FineDetectionMode)
{
this->m_OutputImage->FillBuffer((PixelType)0);
}
if (m_UpwardsExpansion)
{
m_Function->ThresholdBetween(m_MinTH, m_SeedPointValue);
}
else
{
m_Function->ThresholdBetween(m_SeedPointValue, m_MaxTH);
}
this->m_IsAtEnd = true;
seedIndex = m_StartIndices[0]; // warum noch mal? Steht doch schon in Zeile 224
if (this->m_OutputImage->GetBufferedRegion().IsInside(seedIndex) && this->m_SeedPointValue >= this->m_MinTH &&
this->m_SeedPointValue <= this->m_MaxTH)
{
// Push the seed onto the queue
this->InsertIndexTypeIntoQueueMap(m_RegionGrowingState, seedIndex);
// Obviously, we're at the beginning
this->m_IsAtEnd = false;
this->m_OutputImage->SetPixel(seedIndex, (m_InitializeValue - m_RegionGrowingState));
}
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::CheckSeedPointValue()
{
// checks, if the value, that has been averaged over the N-27 neighborhood aorund the seedpoint is still inside
// the thresholds-ranges. if not, the actual value of the seedpoint (not averaged) is used
if (m_SeedPointValue < m_MinTH || m_SeedPointValue > m_MaxTH)
{
m_SeedPointValue = (int)m_Function->GetInputImage()->GetPixel(m_StartIndices[0]);
}
}
template <class TImage, class TFunction>
unsigned int AdaptiveThresholdIterator<TImage, TFunction>::CalculateMaxRGS()
{
if (m_UpwardsExpansion)
{
return (m_MaxTH - m_SeedPointValue);
}
else
{
return (m_SeedPointValue - m_MinTH);
}
}
template <class TImage, class TFunction>
bool AdaptiveThresholdIterator<TImage, TFunction>::IsPixelIncluded(const IndexType &index) const
{
// checks, if grayvalue of current voxel is inside the currently used thresholds
return this->m_Function->EvaluateAtIndex(index);
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::InsertIndexTypeIntoQueueMap(unsigned int key, IndexType index)
{
// first check if the key-specific queue already exists
if (m_QueueMap.count(key) == 0)
{
// if queue doesn´t exist, create it, push the IndexType onto it
// and insert it into the map
IndexQueueType newQueue;
newQueue.push(index);
typedef typename QueueMapType::value_type KeyIndexQueue;
m_QueueMap.insert(KeyIndexQueue(key, newQueue));
}
else
{
// if queue already exists in the map, push IndexType onto its specific queue
(*(m_QueueMap.find(key))).second.push(index);
}
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::DoExtendedFloodStep()
{
// The index in the front of the queue should always be
// valid and be inside since this is what the iterator
// uses in the Set/Get methods. This is ensured by the
// GoToBegin() method.
typename QueueMapType::iterator currentIt = m_QueueMap.find(m_RegionGrowingState);
if (currentIt == m_QueueMap.end())
{
this->IncrementRegionGrowingState();
}
else
{
IndexQueueType *currentQueue = &(*currentIt).second;
// Take the index in the front of the queue
const IndexType &topIndex = currentQueue->front();
// Iterate through all possible dimensions
// NOTE: Replace this with a ShapeNeighborhoodIterator
for (unsigned int i = 0; i < NDimensions; i++)
{
// The j loop establishes either left or right neighbor (+-1)
for (int j = -1; j <= 1; j += 2)
{
IndexType tempIndex;
// build the index of a neighbor
for (unsigned int k = 0; k < NDimensions; k++)
{
if (i != k)
{
- tempIndex.m_Index[k] = topIndex[k];
+ tempIndex[k] = topIndex[k];
}
else
{
- tempIndex.m_Index[k] = topIndex[k] + j;
+ tempIndex[k] = topIndex[k] + j;
}
} // end build the index of a neighbor
// If this is a valid index and have not been tested,
// then test it.
if (m_ImageRegion.IsInside(tempIndex))
{
// check if voxel hasn´t already been processed
if (this->m_OutputImage->GetPixel(tempIndex) == 0)
{
// if it is inside, push it into the queue
if (this->IsPixelIncluded(tempIndex))
{
// hier wird Voxel in momentan aktiven Stack und ins OutputImage geschrieben
this->InsertIndexTypeIntoQueueMap((m_RegionGrowingState), tempIndex);
this->m_OutputImage->SetPixel(tempIndex, (m_InitializeValue - m_RegionGrowingState));
}
else // If the pixel is not inside the current threshold
{
int distance = this->EstimateDistance(
tempIndex); // [!] sollte nicht estimateDistance sondern calculateDistance() heißen!
if (distance != 0)
{
// hier wird Voxel in entsprechenden Stack und ins OutputImage geschrieben
this->InsertIndexTypeIntoQueueMap((distance), tempIndex);
this->m_OutputImage->SetPixel(tempIndex, (m_InitializeValue - distance));
}
}
}
}
} // end left/right neighbor loop
} // end check all neighbors
// Now that all the potential neighbors have been
// inserted we can get rid of the pixel in the front
currentQueue->pop();
m_VoxelCounter++;
if (currentQueue->empty())
{
// if currently used queue is empty
this->IncrementRegionGrowingState();
}
}
if (m_RegionGrowingState >= (m_InitializeValue) || m_DetectionStop)
{
this->m_IsAtEnd = true;
// std::cout << "RegionGrowing finished !" << std::endl;
// std::cout << "Detected point of leakage: " << m_DetectedLeakagePoint << std::endl;
}
}
} // end namespace itk
#endif
diff --git a/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.h b/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.h
index 985cff7fad..d6f2757e67 100644
--- a/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.h
+++ b/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.h
@@ -1,281 +1,281 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
/*===================================================================
This file is based heavily on a corresponding ITK filter.
===================================================================*/
#ifndef __itkContourExtractor2DImageFilter_h
#define __itkContourExtractor2DImageFilter_h
#include "itkConceptChecking.h"
#include "itkImageToPathFilter.h"
#include "itkNumericTraits.h"
#include "itkPolyLineParametricPath.h"
-#include "vcl_deque.h"
-#include "vcl_list.h"
-#include <itksys/hash_map.hxx>
+#include <deque>
+#include <list>
+#include <unordered_map>
namespace itk
{
/** \class ContourExtractor2DImageFilter
* \brief Computes a list of PolyLineParametricPath objects from the contours in
* a 2D image.
*
* Uses the "marching squares" method to compute a the iso-valued contours of
* the input 2D image for a given intensity value. Multiple outputs may be
* produced because an image can have multiple contours at a given level, so it
* is advised to call GetNumberOfOutputs() and GetOutput(n) to retrieve all of
* the contours. The contour value to be extracted can be set with
* SetContourValue(). Image intensities will be linearly interpolated to provide
* sub-pixel resolution for the output contours.
*
* The marching squares algorithm is a special case of the marching cubes
* algorithm (Lorensen, William and Harvey E. Cline. Marching Cubes: A High
* Resolution 3D Surface Construction Algorithm. Computer Graphics (SIGGRAPH 87
* Proceedings) 21(4) July 1987, p. 163-170). A simple explanation is available
* here: http://www.essi.fr/~lingrand/MarchingCubes/algo.html
*
* There is a single ambiguous case in the marching squares algorithm: if a
* given 2x2-pixel square has two high-valued and two low-valued pixels, each
* pair diagonally adjacent. (Where high- and low-valued is with respect to the
* contour value sought.) In this case, either the high-valued pixels can be
* connected into the same "object" (where groups of pixels encircled by a given
* contour are considered an object), or the low-valued pixels can be connected.
* This is the "face connected" versus "face + vertex connected" (or 4- versus
* 4-connected) distinction: high-valued pixels most be treated as one, and
* low-valued as the other. By default, high-valued pixels are treated as
* "face-connected" and low-valued pixels are treated as "face + vertex"
* connected. To reverse this, call VertexConnectHighPixelsOn();
*
* Outputs are not guaranteed to be closed paths: contours which intersect the
* image edge will be left open. All other paths will be closed. (The
* closed-ness of a path can be tested by checking whether the beginning point
* is the same as the end point.)
*
* Produced paths are oriented. Following the path from beginning to end, image
* intensity values lower than the contour value are to the left of the path and
* intensity values grater than the contour value are to the right. In other
* words, the image gradient at a path segment is (approximately) in the direct
* of that segment rotated right by 90 degrees, because the image intensity
* values increase from left-to-right across the segment. This means that the
* generated contours will circle clockwise around "hills" of
* above-contour-value intensity, and counter-clockwise around "depressions" of
* below-contour-value intensity. This convention can be reversed by calling
* ReverseContourOrientationOn().
*
* By default the input image's largest possible region will be processed; call
* SetRequestedRegion() to process a different region, or ClearRequestedRegion()
* to revert to the default value. Note that the requested regions are usually
* set on the output; however since paths have no notion of a "region", this
* must be set at the filter level.
*
* This class was contributed to the Insight Journal by Zachary Pincus.
* http://insight-journal.org/midas/handle.php?handle=1926/165
*
* \sa Image
* \sa Path
* \sa PolyLineParametricPath
*
*/
template <class TInputImage>
class ITK_EXPORT ContourExtractor2DImageFilter : public ImageToPathFilter<TInputImage, PolyLineParametricPath<2>>
{
public:
/** Extract dimension from input and output image. */
itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension);
/** Convenient typedefs for simplifying declarations. */
typedef TInputImage InputImageType;
typedef PolyLineParametricPath<2> OutputPathType;
/** Standard class typedefs. */
typedef ContourExtractor2DImageFilter Self;
typedef ImageToPathFilter<InputImageType, OutputPathType> Superclass;
typedef SmartPointer<Self> Pointer;
typedef SmartPointer<const Self> ConstPointer;
/** Method for creation through the object factory. */
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** Run-time type information (and related methods). */
itkTypeMacro(ContourExtractor2DImageFilter, ImageToPathFilter);
/** Image and path typedef support. */
typedef typename InputImageType::Pointer InputImagePointer;
typedef typename InputImageType::PixelType InputPixelType;
typedef typename InputImageType::IndexType InputIndexType;
typedef typename InputImageType::OffsetType InputOffsetType;
typedef typename InputImageType::RegionType InputRegionType;
typedef typename OutputPathType::Pointer OutputPathPointer;
typedef typename OutputPathType::VertexType VertexType;
typedef typename OutputPathType::VertexListType VertexListType;
/** Real type associated to the input pixel type. */
typedef typename NumericTraits<InputPixelType>::RealType InputRealType;
typedef typename VertexListType::ConstPointer VertexListConstPointer;
/** Control the orientation of the contours with reference to the image
* gradient. (See class documentation.) */
itkSetMacro(ReverseContourOrientation, bool);
itkGetConstReferenceMacro(ReverseContourOrientation, bool);
itkBooleanMacro(ReverseContourOrientation);
/** Control whether high- or low-valued pixels are vertex-connected.
* Default is for low-valued pixels to be vertex-connected.
* (See class documentation.) */
itkSetMacro(VertexConnectHighPixels, bool);
itkGetConstReferenceMacro(VertexConnectHighPixels, bool);
itkBooleanMacro(VertexConnectHighPixels);
/** Control whether the largest possible input region is used, or if a
* custom requested region is to be used. */
void SetRequestedRegion(const InputRegionType region);
itkGetConstReferenceMacro(RequestedRegion, InputRegionType);
void ClearRequestedRegion();
/** Set/Get the image intensity value that the contours should follow.
* This is the equivalent of an iso-value in Marching Squares. */
itkSetMacro(ContourValue, InputRealType);
itkGetConstReferenceMacro(ContourValue, InputRealType);
#ifdef ITK_USE_CONCEPT_CHECKING
/** Begin concept checking */
itkConceptMacro(DimensionShouldBe2, (Concept::SameDimension<itkGetStaticConstMacro(InputImageDimension), 2>));
itkConceptMacro(InputPixelTypeComparable, (Concept::Comparable<InputPixelType>));
itkConceptMacro(InputHasPixelTraitsCheck, (Concept::HasPixelTraits<InputPixelType>));
itkConceptMacro(InputHasNumericTraitsCheck, (Concept::HasNumericTraits<InputPixelType>));
/** End concept checking */
#endif
protected:
ContourExtractor2DImageFilter();
~ContourExtractor2DImageFilter() override;
void PrintSelf(std::ostream &os, Indent indent) const override;
void GenerateData() override;
/** ContourExtractor2DImageFilter manually controls the input requested
* region via SetRequestedRegion and ClearRequestedRegion, so it must
* override the superclass method. */
void GenerateInputRequestedRegion() override;
private:
VertexType InterpolateContourPosition(InputPixelType fromValue,
InputPixelType toValue,
InputIndexType fromIndex,
InputOffsetType toOffset);
void AddSegment(const VertexType from, const VertexType to);
void FillOutputs();
ContourExtractor2DImageFilter(const Self &); // purposely not implemented
void operator=(const Self &); // purposely not implemented
InputRealType m_ContourValue;
bool m_ReverseContourOrientation;
bool m_VertexConnectHighPixels;
bool m_UseCustomRegion;
InputRegionType m_RequestedRegion;
unsigned int m_NumberOfContoursCreated;
// Represent each contour as deque of vertices to facilitate addition of
// nodes at beginning or end. At the end of the processing, we will copy
// the contour into a PolyLineParametricPath.
// We subclass the deque to store an additional bit of information: an
// identification number for each growing contour. We use this number so
// that when it becomes necessary to merge two growing contours, we can
// merge the newer one into the older one. This helps because then we can
// guarantee that the output contour list is ordered from left to right,
// top to bottom (in terms of the first pixel of the contour encountered
// by the marching squares). Currently we make no guarantees that this
// pixel is the first pixel in the contour list, just that the contours
// are so ordered in the output. Ensuring this latter condition (first
// pixel traversed = first pixel in contour) would be possible by either
// changing the merging rules, which would make the contouring operation
// slower, or by storing additional data as to which pixel was first.
- class ContourType : public vcl_deque<VertexType>
+ class ContourType : public std::deque<VertexType>
{
public:
unsigned int m_ContourNumber;
};
// Store all the growing contours in a list. We may need to delete contours
// from anywhere in the sequence (when we merge them together), so we need to
// use a list instead of a vector or similar.
- typedef vcl_list<ContourType> ContourContainer;
+ typedef std::list<ContourType> ContourContainer;
typedef typename ContourContainer::iterator ContourRef;
// declare the hash function we are using for the hash_map.
struct VertexHash
{
typedef typename VertexType::CoordRepType CoordinateType;
inline size_t operator()(const VertexType &k) const
{
// Xor the hashes of the vertices together, after multiplying the
// first by some number, so that identical (x,y) vertex indices
// don't all hash to the same bucket. This is a decent if not
// optimal hash.
const size_t hashVertex1 = this->float_hash(k[0] * 0xbeef);
const size_t hashVertex2 = this->float_hash(k[1]);
const size_t hashValue = hashVertex1 ^ hashVertex2;
return hashValue;
}
// Define hash function for floats. Based on method from
// http://www.brpreiss.com/books/opus4/html/page217.html
inline size_t float_hash(const CoordinateType &k) const
{
if (k == 0)
{
return 0;
}
int exponent;
- CoordinateType mantissa = vcl_frexp(k, &exponent);
- size_t value = static_cast<size_t>(vcl_fabs(mantissa));
+ CoordinateType mantissa = std::frexp(k, &exponent);
+ size_t value = static_cast<size_t>(std::fabs(mantissa));
value = (2 * value - 1) * ~0U;
return value;
}
};
// We use a hash to associate the endpoints of each contour with the
// contour itself. This makes it easy to look up which contour we should add
// a new arc to.
// We can't store the contours themselves in the hashtable because we
// need to have two tables (one to hash from beginpoint -> contour and one
// for endpoint -> contour), and sometimes will remove a contour from the
// tables (if it has been closed or merged with another contour). So in the
// hash table we store a reference to the contour. Because sometimes we will
// need to merge contours, we need to be able to quickly remove contours
// from our list when they have been merged into another. Thus, we store
// an iterator pointing to the contour in the list.
- typedef itksys::hash_map<VertexType, ContourRef, VertexHash> VertexToContourMap;
+ typedef std::unordered_map<VertexType, ContourRef, VertexHash> VertexToContourMap;
typedef typename VertexToContourMap::iterator VertexMapIterator;
typedef typename VertexToContourMap::value_type VertexContourRefPair;
// The contours we find in the image are stored here
ContourContainer m_Contours;
// And indexed by their beginning and ending points here
VertexToContourMap m_ContourStarts;
VertexToContourMap m_ContourEnds;
};
} // end namespace itk
#ifndef ITK_MANUAL_INSTANTIATION
#include "itkContourExtractor2DImageFilter.txx"
#endif
#endif
diff --git a/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.txx b/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.txx
index 77bc5daa67..f103d54896 100644
--- a/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.txx
+++ b/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.txx
@@ -1,536 +1,536 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
/*===================================================================
This file is based heavily on a corresponding ITK filter.
===================================================================*/
#ifndef __itkContourExtractor2DImageFilter_txx
#define __itkContourExtractor2DImageFilter_txx
#include "itkConstShapedNeighborhoodIterator.h"
#include "itkConstShapedNeighborhoodIterator.h"
#include "itkContourExtractor2DImageFilter.h"
#include "itkProgressReporter.h"
-#include "vcl_cmath.h"
+#include <cmath>
namespace itk
{
// Constructor
template <class TInputImage>
ContourExtractor2DImageFilter<TInputImage>::ContourExtractor2DImageFilter()
{
this->m_ContourValue = NumericTraits<InputRealType>::Zero;
this->m_ReverseContourOrientation = false;
this->m_VertexConnectHighPixels = false;
this->m_UseCustomRegion = false;
this->m_NumberOfContoursCreated = 0;
}
// Destructor
template <class TInputImage>
ContourExtractor2DImageFilter<TInputImage>::~ContourExtractor2DImageFilter()
{
}
template <class TInputImage>
void ContourExtractor2DImageFilter<TInputImage>::GenerateData()
{
// Make sure the structures for containing, looking up, and numbering the
// growing contours are empty and ready.
m_Contours.clear();
m_ContourStarts.clear();
m_ContourEnds.clear();
m_NumberOfContoursCreated = 0;
// Set up an iterator to "march the squares" across the image.
// We associate each 2px-by-2px square with the pixel in the upper left of
// that square. We then iterate across the image, examining these 2x2 squares
// and building the contour. By iterating the upper-left pixel of our
// "current square" across every pixel in the image except those on the
// bottom row and rightmost column, we have visited every valid square in the
// image.
InputRegionType region = this->GetInput()->GetRequestedRegion();
typename InputRegionType::SizeType shrunkSize = region.GetSize();
shrunkSize[0] -= 1;
shrunkSize[1] -= 1;
InputRegionType shrunkRegion(region.GetIndex(), shrunkSize);
// Set up a progress reporter
ProgressReporter progress(this, 0, shrunkRegion.GetNumberOfPixels());
// A 1-pixel radius sets up a neighborhood with the following indices:
// 0 1 2
// 3 4 5
// 6 7 8
// We are interested only in the square of 4,5,7,8 which is the 2x2 square
// with the center pixel at the top-left. So we only activate the
// coresponding offsets, and only query pixels 4, 5, 7, and 8 with the
// iterator's GetPixel method.
typedef ConstShapedNeighborhoodIterator<InputImageType> SquareIterator;
typename SquareIterator::RadiusType radius = {{1, 1}};
SquareIterator it(radius, this->GetInput(), shrunkRegion);
InputOffsetType none = {{0, 0}};
InputOffsetType right = {{1, 0}};
InputOffsetType down = {{0, 1}};
InputOffsetType diag = {{1, 1}};
it.ActivateOffset(none);
it.ActivateOffset(right);
it.ActivateOffset(down);
it.ActivateOffset(diag);
for (it.GoToBegin(); !it.IsAtEnd(); ++it)
{
// There are sixteen different possible square types, diagramed below.
// A + indicates that the vertex is above the contour value, and a -
// indicates that the vertex is below or equal to the contour value.
// The vertices of each square are here numbered:
// 01
// 23
// and treated as a binary value with the bits in that order. Thus each
// square can be so numbered:
// 0-- 1+- 2-+ 3++ 4-- 5+- 6-+ 7++
// -- -- -- -- +- +- +- +-
//
// 8-- 9+- 10-+ 11++ 12-- 13+- 14-+ 15++
// -+ -+ -+ -+ ++ ++ ++ ++
//
// The position of the line segment that cuts through (or doesn't, in case
// 0 and 15) each square is clear, except in cases 6 and 9. In this case,
// where the segments are placed is determined by
// m_VertexConnectHighPixels. If m_VertexConnectHighPixels is false, then
// lines like are drawn through square 6, and lines like are drawn through
// square 9. Otherwise, the situation is reversed.
// Finally, recall that we draw the lines so that (moving from tail to
// head) the lower-valued pixels are on the left of the line. So, for
// example, case 1 entails a line slanting from the middle of the top of
// the square to the middle of the left side of the square.
// (1) Determine what number square we are currently inspecting. Remember
// that as far as the neighborhood iterator is concerned, our square
// 01 is numbered as 45
// 23 78
InputPixelType v0, v1, v2, v3;
v0 = it.GetPixel(4);
v1 = it.GetPixel(5);
v2 = it.GetPixel(7);
v3 = it.GetPixel(8);
InputIndexType index = it.GetIndex();
unsigned char squareCase = 0;
if (v0 > m_ContourValue)
squareCase += 1;
if (v1 > m_ContourValue)
squareCase += 2;
if (v2 > m_ContourValue)
squareCase += 4;
if (v3 > m_ContourValue)
squareCase += 8;
// Set up macros to find the ContinuousIndex where the contour intersects
// one of the sides of the square. Normally macros should, of course, be
// eschewed, but since this is an inner loop not calling the function four
// times when two would do is probably worth while. Plus, copy-pasting
// these into the switch below is even worse. InterpolateContourPosition
// takes the values at two vertices, the index of the first vertex, and the
// offset between the two vertices.
#define TOP_ this->InterpolateContourPosition(v0, v1, index, right)
#define BOTTOM_ this->InterpolateContourPosition(v2, v3, index + down, right)
#define LEFT_ this->InterpolateContourPosition(v0, v2, index, down)
#define RIGHT_ this->InterpolateContourPosition(v1, v3, index + right, down)
// (2) Add line segments to the growing contours as defined by the cases.
// AddSegment takes a "from" vertex and a "to" vertex, and adds it to the
// a growing contour, creates a new contour, or merges two together.
switch (squareCase)
{
case 0: // no line
break;
case 1: // top to left
this->AddSegment(TOP_, LEFT_);
break;
case 2: // right to top
this->AddSegment(RIGHT_, TOP_);
break;
case 3: // right to left
this->AddSegment(RIGHT_, LEFT_);
break;
case 4: // left to bottom
this->AddSegment(LEFT_, BOTTOM_);
break;
case 5: // top to bottom
this->AddSegment(TOP_, BOTTOM_);
break;
case 6:
if (m_VertexConnectHighPixels)
{
// left to top
this->AddSegment(LEFT_, TOP_);
// right to bottom
this->AddSegment(RIGHT_, BOTTOM_);
}
else
{
// right to top
this->AddSegment(RIGHT_, TOP_);
// left to bottom
this->AddSegment(LEFT_, BOTTOM_);
}
break;
case 7: // right to bottom
this->AddSegment(RIGHT_, BOTTOM_);
break;
case 8: // bottom to right
this->AddSegment(BOTTOM_, RIGHT_);
break;
case 9:
if (m_VertexConnectHighPixels)
{
// top to right
this->AddSegment(TOP_, RIGHT_);
// bottom to left
this->AddSegment(BOTTOM_, LEFT_);
}
else
{
// top to left
this->AddSegment(TOP_, LEFT_);
// bottom to right
this->AddSegment(BOTTOM_, RIGHT_);
}
break;
case 10: // bottom to top
this->AddSegment(BOTTOM_, TOP_);
break;
case 11: // bottom to left
this->AddSegment(BOTTOM_, LEFT_);
break;
case 12: // left to right
this->AddSegment(LEFT_, RIGHT_);
break;
case 13: // top to right
this->AddSegment(TOP_, RIGHT_);
break;
case 14: // left to top
this->AddSegment(LEFT_, TOP_);
break;
case 15: // no line
break;
} // switch squareCase
progress.CompletedPixel();
} // iteration
// Now create the outputs paths from the deques we've been using.
this->FillOutputs();
m_Contours.clear();
m_ContourStarts.clear();
m_ContourEnds.clear();
m_NumberOfContoursCreated = 0;
}
template <class TInputImage>
inline typename ContourExtractor2DImageFilter<TInputImage>::VertexType
ContourExtractor2DImageFilter<TInputImage>::InterpolateContourPosition(InputPixelType fromValue,
InputPixelType toValue,
InputIndexType fromIndex,
InputOffsetType toOffset)
{
VertexType output;
// Now calculate the fraction of the way from 'from' to 'to' that the contour
// crosses. Interpolate linearly: y = v0 + (v1 - v0) * x, and solve for the
// x that gives y = m_ContourValue: x = (m_ContourValue - v0) / (v1 - v0).
// This assumes that v0 and v1 are separated by exactly ONE unit. So the to
// Offset. value must have exactly one component 1 and the other component 0.
// Also this assumes that fromValue and toValue are different. Otherwise we
// can't interpolate anything!
itkAssertOrThrowMacro((fromValue != toValue), "source and destination are the same");
itkAssertOrThrowMacro(((toOffset[0] == 0 && toOffset[1] == 1) || (toOffset[0] == 1 && toOffset[1] == 0)),
"toOffset has unexpected values");
double x =
(m_ContourValue - static_cast<InputRealType>(fromValue)) / (toValue - static_cast<InputRealType>(fromValue));
output[0] = fromIndex[0] + x * toOffset[0];
output[1] = fromIndex[1] + x * toOffset[1];
return output;
}
template <class TInputImage>
void ContourExtractor2DImageFilter<TInputImage>::AddSegment(VertexType from, VertexType to)
{
if (from == to)
{
// Arc is degenerate: ignore, and the from/two point will be connected
// later by other squares. Degeneracy happens when (and only when) a square
// has exactly one vertex that is the contour value, and the rest are above
// that value.
return;
}
// Try to find an existing contour that starts where the new segment ends.
VertexMapIterator newTail = m_ContourStarts.find(to);
// Try to find an existing contour that ends where the new segment starts.
VertexMapIterator newHead = m_ContourEnds.find(from);
if (newTail != m_ContourStarts.end() && newHead != m_ContourEnds.end())
{
// We need to connect these two contours. The act of connecting them will
// add the needed arc.
auto tail = newTail->second;
itkAssertOrThrowMacro((tail->front() == to), "End doesn't match Beginning");
auto head = newHead->second;
itkAssertOrThrowMacro((head->back() == from), "Beginning doesn't match End");
if (head == tail)
{
// We've closed a contour. Add the end point, and remove from the maps
head->push_back(to);
m_ContourStarts.erase(newTail);
// erase the front of tail. Because head and tail are the same contour,
// don't worry about erasing the front of head!
m_ContourEnds.erase(newHead); // erase the end of head/tail.
}
else
{
// We have found two distinct contours that need to be joined. Careful
// here: we want to keep the first segment in the list when merging so
// that contours are always returned in top-to-bottom, right-to-left
// order (with regard to the image pixel found to be inside the contour).
if (tail->m_ContourNumber > head->m_ContourNumber)
{
// if tail was created later than head...
// Copy tail to the end of head and remove
// tail from everything.
head->insert(head->end(), tail->begin(), tail->end());
// Now remove 'tail' from the list and the maps because it has been
// subsumed.
m_ContourStarts.erase(newTail);
int erased = m_ContourEnds.erase(tail->back());
// There should be exactly one entry in the hash for that endpoint
if (erased != 1)
{
itkWarningMacro(<< "There should be exactly one entry in the hash for that endpoint, but there are "
<< erased);
}
m_Contours.erase(tail); // remove from the master list
// Now remove the old end of 'head' from the ends map and add
// the new end.
m_ContourEnds.erase(newHead);
m_ContourEnds.insert(VertexContourRefPair(head->back(), head));
}
else
{
// Copy head to the beginning of tail and remove
// head from everything.
tail->insert(tail->begin(), head->begin(), head->end());
// Now remove 'head' from the list and the maps because
// it has been subsumed.
m_ContourEnds.erase(newHead);
int erased = m_ContourStarts.erase(head->front());
if (erased != 1)
{
itkWarningMacro(<< "There should be exactly one entry in the hash for that endpoint, but there are "
<< erased);
}
m_Contours.erase(head); // remove from the master list
// Now remove the old start of 'tail' from the starts map and
// add the new start.
m_ContourStarts.erase(newTail);
m_ContourStarts.insert(VertexContourRefPair(tail->front(), tail));
}
}
}
else if (newTail == m_ContourStarts.end() && newHead == m_ContourEnds.end())
{
// No contours found: add a new one.
// Make it on the heap. It will be copied into m_Contours.
ContourType contour;
// Add the endpoints
contour.push_front(from);
contour.push_back(to);
contour.m_ContourNumber = m_NumberOfContoursCreated++;
// Add the contour to the end of the list and get a reference to it.
m_Contours.push_back(contour);
// recall that end() is an iterator to one past the back!
auto newContour = --m_Contours.end();
// add the endpoints and an iterator pointing to the contour
// in the list to the maps.
m_ContourStarts.insert(VertexContourRefPair(from, newContour));
m_ContourEnds.insert(VertexContourRefPair(to, newContour));
}
else if (newTail != m_ContourStarts.end() && newHead == m_ContourEnds.end())
{
// Found a single contour to which the new arc should be prepended.
auto tail = newTail->second;
itkAssertOrThrowMacro((tail->front() == to), "End doesn't match Beginning");
tail->push_front(from);
// erase the old start of this contour
m_ContourStarts.erase(newTail);
// Now add the new start of this contour.
m_ContourStarts.insert(VertexContourRefPair(from, tail));
}
else if (newTail == m_ContourStarts.end() && newHead != m_ContourEnds.end())
{
// Found a single contour to which the new arc should be appended.
auto head = newHead->second;
itkAssertOrThrowMacro((head->back() == from), "Beginning doesn't match End");
head->push_back(to);
// erase the old end of this contour
m_ContourEnds.erase(newHead);
// Now add the new start of this contour.
m_ContourEnds.insert(VertexContourRefPair(to, head));
}
}
template <class TInputImage>
void ContourExtractor2DImageFilter<TInputImage>::FillOutputs()
{
this->SetNumberOfIndexedOutputs(m_Contours.size());
int i = 0;
for (auto it = m_Contours.begin(); it != m_Contours.end(); it++, i++)
{
OutputPathPointer output = this->GetOutput(i);
if (output.IsNull())
{
// Static cast is OK because we know PathSource will make its templated
// class type
output = static_cast<OutputPathType *>(this->MakeOutput(i).GetPointer());
this->SetNthOutput(i, output.GetPointer());
}
typename VertexListType::Pointer path = const_cast<VertexListType *>(output->GetVertexList());
path->Initialize();
path->reserve(it->size()); // use std::vector version of 'reserve()'
// instead of VectorContainer::Reserve() to work around
// the fact that the latter is essentially std::vector::resize(),
// which is not what we want.
// Now put all the points from the contour deque into the path and
// mark output as modified
typedef typename ContourType::const_iterator ConstIteratorType;
if (m_ReverseContourOrientation)
{
ConstIteratorType itC = (*it).end();
do
{
itC--;
path->push_back(*itC);
} while (itC != (*it).begin());
}
else
{
ConstIteratorType itC = (*it).begin();
while (itC != (*it).end())
{
path->push_back(*itC);
itC++;
}
}
output->Modified();
}
}
template <class TInputImage>
void ContourExtractor2DImageFilter<TInputImage>::SetRequestedRegion(const InputRegionType region)
{
itkDebugMacro("setting RequestedRegion to " << region);
m_UseCustomRegion = true;
if (this->m_RequestedRegion != region)
{
this->m_RequestedRegion = region;
this->Modified();
}
}
template <class TInputImage>
void ContourExtractor2DImageFilter<TInputImage>::ClearRequestedRegion()
{
itkDebugMacro("Clearing RequestedRegion.");
if (this->m_UseCustomRegion == true)
{
this->m_UseCustomRegion = false;
this->Modified();
}
}
template <class TInputImage>
void ContourExtractor2DImageFilter<TInputImage>::GenerateInputRequestedRegion()
{
InputImageType *input = const_cast<InputImageType *>(this->GetInput());
if (!input)
return;
if (m_UseCustomRegion)
{
InputRegionType requestedRegion = m_RequestedRegion;
if (requestedRegion.Crop(input->GetLargestPossibleRegion()))
{
input->SetRequestedRegion(requestedRegion);
return;
}
else
{
// Couldn't crop the region (requested region is outside the largest
// possible region). Throw an exception.
// store what we tried to request (prior to trying to crop)
input->SetRequestedRegion(requestedRegion);
// build an exception
InvalidRequestedRegionError e(__FILE__, __LINE__);
e.SetLocation(ITK_LOCATION);
e.SetDescription("Requested region is outside the largest possible region.");
e.SetDataObject(input);
throw e;
}
}
else
{
input->SetRequestedRegion(input->GetLargestPossibleRegion());
}
}
/**
* Standard "PrintSelf" method
*/
template <class TInputImage>
void ContourExtractor2DImageFilter<TInputImage>::PrintSelf(std::ostream &os, Indent indent) const
{
Superclass::PrintSelf(os, indent);
os << indent << "ReverseContourOrientation: " << m_ReverseContourOrientation << std::endl;
os << indent << "VertexConnectHighPixels: " << m_VertexConnectHighPixels << std::endl;
os << indent << "UseCustomRegion: " << m_UseCustomRegion << std::endl;
os << indent << "NumericTraits: " << m_UseCustomRegion << std::endl;
os << indent << "NumberOfContoursCreated: " << m_NumberOfContoursCreated << std::endl;
if (m_UseCustomRegion)
{
os << indent << "Custom region: " << m_RequestedRegion << std::endl;
}
typedef typename NumericTraits<InputRealType>::PrintType InputRealPrintType;
os << indent << "Contour value: " << static_cast<InputRealPrintType>(m_ContourValue) << std::endl;
}
} // end namespace itk
#endif
diff --git a/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp
index 27a8beb883..86e8a0b011 100644
--- a/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp
+++ b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp
@@ -1,486 +1,486 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkCorrectorAlgorithm.h"
#include "mitkContourUtils.h"
#include "mitkITKImageImport.h"
#include "mitkImageAccessByItk.h"
#include "mitkImageCast.h"
#include "mitkImageDataItem.h"
#include <mitkContourModelUtils.h>
#include "itkCastImageFilter.h"
#include "itkImageDuplicator.h"
#include "itkImageRegionIterator.h"
mitk::CorrectorAlgorithm::CorrectorAlgorithm() : ImageToImageFilter(), m_FillColor(1), m_EraseColor(0)
{
}
mitk::CorrectorAlgorithm::~CorrectorAlgorithm()
{
}
template <typename TPixel, unsigned int VDimensions>
void ConvertBackToCorrectPixelType(
itk::Image<TPixel, VDimensions> *,
mitk::Image::Pointer target,
itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2>::Pointer segmentationPixelTypeImage)
{
typedef itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2> InputImageType;
typedef itk::Image<TPixel, 2> OutputImageType;
typedef itk::CastImageFilter<InputImageType, OutputImageType> CastImageFilterType;
typename CastImageFilterType::Pointer castImageFilter = CastImageFilterType::New();
castImageFilter->SetInput(segmentationPixelTypeImage);
castImageFilter->Update();
typename OutputImageType::Pointer tempItkImage = castImageFilter->GetOutput();
tempItkImage->DisconnectPipeline();
mitk::CastToMitkImage(tempItkImage, target);
}
void mitk::CorrectorAlgorithm::GenerateData()
{
Image::Pointer inputImage = ImageToImageFilter::GetInput(0);
if (inputImage.IsNull() || inputImage->GetDimension() != 2)
{
itkExceptionMacro("CorrectorAlgorithm needs a 2D image as input.");
}
if (m_Contour.IsNull())
{
itkExceptionMacro("CorrectorAlgorithm needs a Contour object as input.");
}
// copy the input (since m_WorkingImage will be changed later)
m_WorkingImage = inputImage;
TimeGeometry::Pointer originalGeometry = nullptr;
if (inputImage->GetTimeGeometry())
{
originalGeometry = inputImage->GetTimeGeometry()->Clone();
m_WorkingImage->SetTimeGeometry(originalGeometry);
}
else
{
itkExceptionMacro("Original image does not have a 'Time sliced geometry'! Cannot copy.");
}
Image::Pointer temporarySlice;
// Convert to DefaultSegmentationDataType (because TobiasHeimannCorrectionAlgorithm relys on that data type)
{
itk::Image<DefaultSegmentationDataType, 2>::Pointer correctPixelTypeImage;
CastToItkImage(m_WorkingImage, correctPixelTypeImage);
assert(correctPixelTypeImage.IsNotNull());
// possible bug in CastToItkImage ?
// direction maxtrix is wrong/broken/not working after CastToItkImage, leading to a failed assertion in
// mitk/Core/DataStructures/mitkSlicedGeometry3D.cpp, 479:
// virtual void mitk::SlicedGeometry3D::SetSpacing(const mitk::Vector3D&): Assertion `aSpacing[0]>0 && aSpacing[1]>0
// && aSpacing[2]>0' failed
// solution here: we overwrite it with an unity matrix
itk::Image<DefaultSegmentationDataType, 2>::DirectionType imageDirection;
imageDirection.SetIdentity();
// correctPixelTypeImage->SetDirection(imageDirection);
temporarySlice = this->GetOutput();
// temporarySlice = ImportItkImage( correctPixelTypeImage );
// m_FillColor = 1;
m_EraseColor = 0;
ImprovedHeimannCorrectionAlgorithm(correctPixelTypeImage);
// this is suboptimal, needs to be kept synchronous to DefaultSegmentationDataType
- if (inputImage->GetChannelDescriptor().GetPixelType().GetComponentType() == itk::ImageIOBase::USHORT)
+ if (inputImage->GetChannelDescriptor().GetPixelType().GetComponentType() == itk::IOComponentEnum::USHORT)
{ // the cast at the beginning did not copy the data
CastToMitkImage(correctPixelTypeImage, temporarySlice);
}
else
{ // it did copy the data and cast the pixel type
AccessByItk_n(m_WorkingImage, ConvertBackToCorrectPixelType, (temporarySlice, correctPixelTypeImage));
}
}
temporarySlice->SetTimeGeometry(originalGeometry);
}
template <typename ScalarType>
itk::Index<2> mitk::CorrectorAlgorithm::ensureIndexInImage(ScalarType i0, ScalarType i1)
{
itk::Index<2> toReturn;
itk::Size<5> size = m_WorkingImage->GetLargestPossibleRegion().GetSize();
toReturn[0] = std::min((ScalarType)(size[0] - 1), std::max((ScalarType)0.0, i0));
toReturn[1] = std::min((ScalarType)(size[1] - 1), std::max((ScalarType)0.0, i1));
return toReturn;
}
bool mitk::CorrectorAlgorithm::ImprovedHeimannCorrectionAlgorithm(
itk::Image<DefaultSegmentationDataType, 2>::Pointer pic)
{
/*!
Some documentation (not by the original author)
TobiasHeimannCorrectionAlgorithm will be called, when the user has finished drawing a freehand line.
There should be different results, depending on the line's properties:
1. Without any prior segmentation, the start point and the end point of the drawn line will be
connected to a contour and the area enclosed by the contour will be marked as segmentation.
2. When the whole line is inside a segmentation, start and end point will be connected to
a contour and the area of this contour will be subtracted from the segmentation.
3. When the line starts inside a segmentation and ends outside with only a single
transition from segmentation to no-segmentation, nothing will happen.
4. When there are multiple transitions between inside-segmentation and
outside-segmentation, the line will be divided in so called segments. Each segment is
either fully inside or fully outside a segmentation. When it is inside a segmentation, its
enclosed area will be subtracted from the segmentation. When the segment is outside a
segmentation, its enclosed area it will be added to the segmentation.
The algorithm is described in full length in Tobias Heimann's diploma thesis
(MBI Technical Report 145, p. 37 - 40).
*/
ContourModel::Pointer projectedContour =
mitk::ContourModelUtils::ProjectContourTo2DSlice(m_WorkingImage, m_Contour);
if (projectedContour.IsNull() || projectedContour->GetNumberOfVertices() < 2)
return false;
// Read the first point of the contour
auto contourIter = projectedContour->Begin();
if (contourIter == projectedContour->End())
return false;
itk::Index<2> previousIndex;
previousIndex = ensureIndexInImage((*contourIter)->Coordinates[0], (*contourIter)->Coordinates[1]);
++contourIter;
int currentColor = (pic->GetPixel(previousIndex) == m_FillColor);
TSegData currentSegment;
int countOfSegments = 1;
bool firstSegment = true;
auto contourEnd = projectedContour->End();
for (; contourIter != contourEnd; ++contourIter)
{
// Get current point
itk::Index<2> currentIndex;
currentIndex = ensureIndexInImage((*contourIter)->Coordinates[0] + 0.5, (*contourIter)->Coordinates[1] + 0.5);
// Calculate length and slope
double slopeX = currentIndex[0] - previousIndex[0];
double slopeY = currentIndex[1] - previousIndex[1];
double length = std::sqrt(slopeX * slopeX + slopeY * slopeY);
double deltaX = slopeX / length;
double deltaY = slopeY / length;
for (double i = 0; i <= length && length > 0; i += 1)
{
itk::Index<2> temporaryIndex;
temporaryIndex = ensureIndexInImage(previousIndex[0] + deltaX * i, previousIndex[1] + deltaY * i);
if (!pic->GetLargestPossibleRegion().IsInside(temporaryIndex))
continue;
if ((pic->GetPixel(temporaryIndex) == m_FillColor) != currentColor)
{
currentSegment.points.push_back(temporaryIndex);
if (!firstSegment)
{
ModifySegment(currentSegment, pic);
}
else
{
firstSegment = false;
}
currentSegment = TSegData();
++countOfSegments;
currentColor = (pic->GetPixel(temporaryIndex) == m_FillColor);
}
currentSegment.points.push_back(temporaryIndex);
}
previousIndex = currentIndex;
}
return true;
}
void mitk::CorrectorAlgorithm::ColorSegment(
const mitk::CorrectorAlgorithm::TSegData &segment,
itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2>::Pointer pic)
{
int colorMode = (pic->GetPixel(segment.points[0]) == m_FillColor);
int color = 0;
if (colorMode)
color = m_EraseColor;
else
color = m_FillColor;
std::vector<itk::Index<2>>::const_iterator indexIterator;
std::vector<itk::Index<2>>::const_iterator indexEnd;
indexIterator = segment.points.begin();
indexEnd = segment.points.end();
for (; indexIterator != indexEnd; ++indexIterator)
{
pic->SetPixel(*indexIterator, color);
}
}
itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2>::Pointer mitk::CorrectorAlgorithm::CloneImage(
itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2>::Pointer pic)
{
typedef itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2> ItkImageType;
typedef itk::ImageDuplicator<ItkImageType> DuplicatorType;
DuplicatorType::Pointer duplicator = DuplicatorType::New();
duplicator->SetInputImage(pic);
duplicator->Update();
return duplicator->GetOutput();
}
itk::Index<2> mitk::CorrectorAlgorithm::GetFirstPoint(
const mitk::CorrectorAlgorithm::TSegData &segment,
itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2>::Pointer pic)
{
int colorMode = (pic->GetPixel(segment.points[0]) == m_FillColor);
std::vector<itk::Index<2>>::const_iterator indexIterator;
std::vector<itk::Index<2>>::const_iterator indexEnd;
indexIterator = segment.points.begin();
indexEnd = segment.points.end();
itk::Index<2> index;
for (; indexIterator != indexEnd; ++indexIterator)
{
for (int xOffset = -1; xOffset < 2; ++xOffset)
{
for (int yOffset = -1; yOffset < 2; ++yOffset)
{
index = ensureIndexInImage((*indexIterator)[0] - xOffset, (*indexIterator)[1] - yOffset);
if ((pic->GetPixel(index) == m_FillColor) != colorMode)
{
return index;
}
}
}
}
mitkThrow() << "No Starting point is found next to the curve.";
}
std::vector<itk::Index<2>> mitk::CorrectorAlgorithm::FindSeedPoints(
const mitk::CorrectorAlgorithm::TSegData &segment,
itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2>::Pointer pic)
{
typedef itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2>::Pointer ItkImagePointerType;
std::vector<itk::Index<2>> seedPoints;
try
{
itk::Index<2> firstPoint = GetFirstPoint(segment, pic);
seedPoints.push_back(firstPoint);
}
catch (const mitk::Exception&)
{
return seedPoints;
}
if (segment.points.size() < 4)
return seedPoints;
std::vector<itk::Index<2>>::const_iterator indexIterator;
std::vector<itk::Index<2>>::const_iterator indexEnd;
indexIterator = segment.points.begin();
indexEnd = segment.points.end();
ItkImagePointerType listOfPoints = CloneImage(pic);
listOfPoints->FillBuffer(0);
listOfPoints->SetPixel(seedPoints[0], 1);
for (; indexIterator != indexEnd; ++indexIterator)
{
listOfPoints->SetPixel(*indexIterator, 2);
}
indexIterator = segment.points.begin();
indexIterator++;
indexIterator++;
indexEnd--;
indexEnd--;
for (; indexIterator != indexEnd; ++indexIterator)
{
bool pointFound = true;
while (pointFound)
{
pointFound = false;
itk::Index<2> index;
itk::Index<2> index2;
for (int xOffset = -1; xOffset < 2; ++xOffset)
{
for (int yOffset = -1; yOffset < 2; ++yOffset)
{
index = ensureIndexInImage((*indexIterator)[0] - xOffset, (*indexIterator)[1] - yOffset);
index2 = index;
if (listOfPoints->GetPixel(index2) > 0)
continue;
index[0] = index[0] - 1;
index = ensureIndexInImage(index[0], index[1]);
if (listOfPoints->GetPixel(index) == 1)
{
pointFound = true;
seedPoints.push_back(index2);
listOfPoints->SetPixel(index2, 1);
continue;
}
index[0] = index[0] + 2;
index = ensureIndexInImage(index[0], index[1]);
if (listOfPoints->GetPixel(index) == 1)
{
pointFound = true;
seedPoints.push_back(index2);
listOfPoints->SetPixel(index2, 1);
continue;
}
index[0] = index[0] - 1;
index[1] = index[1] - 1;
index = ensureIndexInImage(index[0], index[1]);
if (listOfPoints->GetPixel(index) == 1)
{
pointFound = true;
seedPoints.push_back(index2);
listOfPoints->SetPixel(index2, 1);
continue;
}
index[1] = index[1] + 2;
index = ensureIndexInImage(index[0], index[1]);
if (listOfPoints->GetPixel(index) == 1)
{
pointFound = true;
seedPoints.push_back(index2);
listOfPoints->SetPixel(index2, 1);
continue;
}
}
}
}
}
return seedPoints;
}
int mitk::CorrectorAlgorithm::FillRegion(
const std::vector<itk::Index<2>> &seedPoints,
itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2>::Pointer pic)
{
int numberOfPixel = 0;
int mode = (pic->GetPixel(seedPoints[0]) == m_FillColor);
int drawColor = m_FillColor;
if (mode)
{
drawColor = m_EraseColor;
}
std::vector<itk::Index<2>> workPoints;
workPoints = seedPoints;
// workPoints.push_back(seedPoints[0]);
while (workPoints.size() > 0)
{
itk::Index<2> currentIndex = workPoints.back();
workPoints.pop_back();
if ((pic->GetPixel(currentIndex) == m_FillColor) == mode)
++numberOfPixel;
pic->SetPixel(currentIndex, drawColor);
currentIndex = ensureIndexInImage(currentIndex[0] - 1, currentIndex[1]);
if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == m_FillColor) == mode)
workPoints.push_back(currentIndex);
currentIndex = ensureIndexInImage(currentIndex[0] + 2, currentIndex[1]);
if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == m_FillColor) == mode)
workPoints.push_back(currentIndex);
currentIndex = ensureIndexInImage(currentIndex[0] - 1, currentIndex[1] - 1);
if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == m_FillColor) == mode)
workPoints.push_back(currentIndex);
currentIndex = ensureIndexInImage(currentIndex[0], currentIndex[1] + 2);
if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == m_FillColor) == mode)
workPoints.push_back(currentIndex);
}
return numberOfPixel;
}
void mitk::CorrectorAlgorithm::OverwriteImage(
itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2>::Pointer source,
itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2>::Pointer target)
{
typedef itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2> ItkImageType;
typedef itk::ImageRegionIterator<ItkImageType> ImageIteratorType;
ImageIteratorType sourceIter(source, source->GetLargestPossibleRegion());
ImageIteratorType targetIter(target, target->GetLargestPossibleRegion());
while (!sourceIter.IsAtEnd())
{
targetIter.Set(sourceIter.Get());
++sourceIter;
++targetIter;
}
}
bool mitk::CorrectorAlgorithm::ModifySegment(const TSegData &segment,
itk::Image<DefaultSegmentationDataType, 2>::Pointer pic)
{
typedef itk::Image<DefaultSegmentationDataType, 2>::Pointer ItkImagePointerType;
ItkImagePointerType firstSideImage = CloneImage(pic);
ColorSegment(segment, firstSideImage);
ItkImagePointerType secondSideImage = CloneImage(firstSideImage);
std::vector<itk::Index<2>> seedPoints = FindSeedPoints(segment, firstSideImage);
if (seedPoints.size() < 1)
return false;
int firstSidePixel = FillRegion(seedPoints, firstSideImage);
std::vector<itk::Index<2>> secondSeedPoints = FindSeedPoints(segment, firstSideImage);
if (secondSeedPoints.size() < 1)
return false;
int secondSidePixel = FillRegion(secondSeedPoints, secondSideImage);
if (firstSidePixel < secondSidePixel)
{
OverwriteImage(firstSideImage, pic);
}
else
{
OverwriteImage(secondSideImage, pic);
}
return true;
}
diff --git a/Modules/Segmentation/Algorithms/mitkDiffImageApplier.cpp b/Modules/Segmentation/Algorithms/mitkDiffImageApplier.cpp
index 86eebc1701..0d009c3680 100644
--- a/Modules/Segmentation/Algorithms/mitkDiffImageApplier.cpp
+++ b/Modules/Segmentation/Algorithms/mitkDiffImageApplier.cpp
@@ -1,359 +1,359 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkDiffImageApplier.h"
#include "mitkApplyDiffImageOperation.h"
#include "mitkImageAccessByItk.h"
#include "mitkImageCast.h"
#include "mitkImageTimeSelector.h"
#include "mitkRenderingManager.h"
#include "mitkSegmentationInterpolationController.h"
#include <itkImageRegionConstIterator.h>
#include <itkImageSliceIteratorWithIndex.h>
#include <type_traits>
mitk::DiffImageApplier::DiffImageApplier()
: m_Image(nullptr),
m_SliceDifferenceImage(nullptr),
m_SliceIndex(0),
m_SliceDimension(0),
m_TimeStep(0),
m_Dimension0(0),
m_Dimension1(0),
m_Factor(1.0)
{
}
mitk::DiffImageApplier::~DiffImageApplier()
{
}
void mitk::DiffImageApplier::ExecuteOperation(Operation *operation)
{
auto *imageOperation = dynamic_cast<ApplyDiffImageOperation *>(operation);
if (imageOperation // we actually have the kind of operation that we can handle
&&
imageOperation->IsImageStillValid()) // AND the image is not yet deleted
{
m_Image = imageOperation->GetImage();
Image::Pointer image3D = m_Image; // will be changed later in case of 3D+t
m_SliceDifferenceImage = imageOperation->GetDiffImage();
m_TimeStep = imageOperation->GetTimeStep();
m_Factor = imageOperation->GetFactor();
if (m_SliceDifferenceImage->GetDimension() == 2)
{
m_SliceIndex = imageOperation->GetSliceIndex();
m_SliceDimension = imageOperation->GetSliceDimension();
switch (m_SliceDimension)
{
default:
case 2:
m_Dimension0 = 0;
m_Dimension1 = 1;
break;
case 1:
m_Dimension0 = 0;
m_Dimension1 = 2;
break;
case 0:
m_Dimension0 = 1;
m_Dimension1 = 2;
break;
}
if (m_SliceDifferenceImage->GetDimension() != 2 || (m_Image->GetDimension() < 3 || m_Image->GetDimension() > 4) ||
m_SliceDifferenceImage->GetDimension(0) != m_Image->GetDimension(m_Dimension0) ||
m_SliceDifferenceImage->GetDimension(1) != m_Image->GetDimension(m_Dimension1) ||
m_SliceIndex >= m_Image->GetDimension(m_SliceDimension))
{
itkExceptionMacro(
"Slice and image dimensions differ or slice index is too large. Sorry, cannot work like this.");
return;
}
if (m_Image->GetDimension() == 4)
{
ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New();
timeSelector->SetInput(m_Image);
timeSelector->SetTimeNr(m_TimeStep);
timeSelector->UpdateLargestPossibleRegion();
image3D = timeSelector->GetOutput();
}
// this will do a long long if/else to find out both pixel types
AccessFixedDimensionByItk(image3D, ItkImageSwitch2DDiff, 3);
if (m_Factor == 1 || m_Factor == -1)
{
if (m_Factor == -1)
{
// multiply diff pixels by factor and then send this diff slice
AccessFixedDimensionByItk(m_SliceDifferenceImage, ItkInvertPixelValues, 2);
}
// just send the diff to SegmentationInterpolationController
SegmentationInterpolationController *interpolator =
SegmentationInterpolationController::InterpolatorForImage(m_Image);
if (interpolator)
{
interpolator->BlockModified(true);
interpolator->SetChangedSlice(m_SliceDifferenceImage, m_SliceDimension, m_SliceIndex, m_TimeStep);
}
m_Image->Modified();
if (interpolator)
{
interpolator->BlockModified(false);
}
if (m_Factor == -1) // return to normal values
{
AccessFixedDimensionByItk(m_SliceDifferenceImage, ItkInvertPixelValues, 2);
}
}
else // no trivial case, too lazy to do something else
{
m_Image->Modified(); // check if interpolation is called. prefer to send diff directly
}
RenderingManager::GetInstance()->RequestUpdateAll();
}
else if (m_SliceDifferenceImage->GetDimension() == 3)
{
// ...
if (m_SliceDifferenceImage->GetDimension(0) != m_Image->GetDimension(0) ||
m_SliceDifferenceImage->GetDimension(1) != m_Image->GetDimension(1) ||
m_SliceDifferenceImage->GetDimension(2) != m_Image->GetDimension(2) || m_TimeStep >= m_Image->GetDimension(3))
{
itkExceptionMacro("Diff image size differs from original image size. Sorry, cannot work like this.");
return;
}
if (m_Image->GetDimension() == 4)
{
ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New();
timeSelector->SetInput(m_Image);
timeSelector->SetTimeNr(m_TimeStep);
timeSelector->UpdateLargestPossibleRegion();
image3D = timeSelector->GetOutput();
}
// this will do a long long if/else to find out both pixel types
AccessFixedDimensionByItk(image3D, ItkImageSwitch3DDiff, 3);
if (m_Factor == 1 || m_Factor == -1)
{
if (m_Factor == -1)
{
// multiply diff pixels by factor and then send this diff slice
AccessFixedDimensionByItk(m_SliceDifferenceImage, ItkInvertPixelValues, 3);
}
// just send the diff to SegmentationInterpolationController
SegmentationInterpolationController *interpolator =
SegmentationInterpolationController::InterpolatorForImage(m_Image);
if (interpolator)
{
interpolator->BlockModified(true);
interpolator->SetChangedVolume(m_SliceDifferenceImage, m_TimeStep);
}
m_Image->Modified();
if (interpolator)
{
interpolator->BlockModified(false);
}
if (m_Factor == -1) // return to normal values
{
AccessFixedDimensionByItk(m_SliceDifferenceImage, ItkInvertPixelValues, 3);
}
}
else // no trivial case, too lazy to do something else
{
m_Image->Modified(); // check if interpolation is called. prefer to send diff directly
}
RenderingManager::GetInstance()->RequestUpdateAll();
}
else
{
itkExceptionMacro("Diff image must be 2D or 3D. Sorry, cannot work like this.");
return;
}
}
m_Image = nullptr;
m_SliceDifferenceImage = nullptr;
}
mitk::DiffImageApplier *mitk::DiffImageApplier::GetInstanceForUndo()
{
static DiffImageApplier::Pointer s_Instance = DiffImageApplier::New();
return s_Instance;
}
// basically copied from mitk/Core/Algorithms/mitkImageAccessByItk.h
#define myMITKDiffImageApplierFilterAccessByItk(mitkImage, itkImageTypeFunction, pixeltype, dimension, itkimage2) \
if (typeId == MapPixelComponentType<pixeltype>::value) \
\
{ \
typedef itk::Image<pixeltype, dimension> ImageType; \
typedef mitk::ImageToItk<ImageType> ImageToItkType; \
itk::SmartPointer<ImageToItkType> imagetoitk = ImageToItkType::New(); \
const mitk::Image *constImage = mitkImage; \
mitk::Image *nonConstImage = const_cast<mitk::Image *>(constImage); \
nonConstImage->Update(); \
imagetoitk->SetInput(nonConstImage); \
imagetoitk->Update(); \
itkImageTypeFunction(imagetoitk->GetOutput(), itkimage2); \
\
}
#define myMITKDiffImageApplierFilterAccessAllTypesByItk(mitkImage, itkImageTypeFunction, dimension, itkimage2) \
\
{ \
myMITKDiffImageApplierFilterAccessByItk(mitkImage, itkImageTypeFunction, double, dimension, itkimage2) else myMITKDiffImageApplierFilterAccessByItk( \
mitkImage, \
itkImageTypeFunction, \
float, \
dimension, \
itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, itkImageTypeFunction, int, dimension, itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, \
itkImageTypeFunction, \
unsigned int, \
dimension, \
itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, itkImageTypeFunction, short, dimension, itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, itkImageTypeFunction, unsigned short, dimension, itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, \
itkImageTypeFunction, \
char, \
dimension, \
itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, \
itkImageTypeFunction, \
unsigned char, \
dimension, \
itkimage2) \
\
}
template <typename TPixel, unsigned int VImageDimension>
void mitk::DiffImageApplier::ItkImageSwitch2DDiff(itk::Image<TPixel, VImageDimension> *itkImage)
{
- const int typeId = m_SliceDifferenceImage->GetPixelType().GetComponentType();
+ const auto typeId = m_SliceDifferenceImage->GetPixelType().GetComponentType();
myMITKDiffImageApplierFilterAccessAllTypesByItk(m_SliceDifferenceImage, ItkImageProcessing2DDiff, 2, itkImage);
}
template <typename TPixel, unsigned int VImageDimension>
void mitk::DiffImageApplier::ItkImageSwitch3DDiff(itk::Image<TPixel, VImageDimension> *itkImage)
{
- const int typeId = m_SliceDifferenceImage->GetPixelType().GetComponentType();
+ const auto typeId = m_SliceDifferenceImage->GetPixelType().GetComponentType();
myMITKDiffImageApplierFilterAccessAllTypesByItk(m_SliceDifferenceImage, ItkImageProcessing3DDiff, 3, itkImage);
}
template <typename TPixel1, unsigned int VImageDimension1, typename TPixel2, unsigned int VImageDimension2>
void mitk::DiffImageApplier::ItkImageProcessing2DDiff(itk::Image<TPixel1, VImageDimension1> *diffImage,
itk::Image<TPixel2, VImageDimension2> *outputImage)
{
typedef itk::Image<TPixel1, VImageDimension1> DiffImageType;
typedef itk::Image<TPixel2, VImageDimension2> VolumeImageType;
typedef itk::ImageSliceIteratorWithIndex<VolumeImageType> OutputSliceIteratorType;
typedef itk::ImageRegionConstIterator<DiffImageType> DiffSliceIteratorType;
typename VolumeImageType::RegionType sliceInVolumeRegion;
sliceInVolumeRegion = outputImage->GetLargestPossibleRegion();
sliceInVolumeRegion.SetSize(m_SliceDimension, 1); // just one slice
sliceInVolumeRegion.SetIndex(m_SliceDimension, m_SliceIndex); // exactly this slice, please
OutputSliceIteratorType outputIterator(outputImage, sliceInVolumeRegion);
outputIterator.SetFirstDirection(m_Dimension0);
outputIterator.SetSecondDirection(m_Dimension1);
DiffSliceIteratorType diffIterator(diffImage, diffImage->GetLargestPossibleRegion());
// iterate over output slice (and over input slice simultaneously)
outputIterator.GoToBegin();
diffIterator.GoToBegin();
while (!outputIterator.IsAtEnd())
{
while (!outputIterator.IsAtEndOfSlice())
{
while (!outputIterator.IsAtEndOfLine())
{
TPixel2 newValue = outputIterator.Get() + (TPixel2)((double)diffIterator.Get() * m_Factor);
outputIterator.Set(newValue);
++outputIterator;
++diffIterator;
}
outputIterator.NextLine();
}
outputIterator.NextSlice();
}
}
template <typename TPixel1, unsigned int VImageDimension1, typename TPixel2, unsigned int VImageDimension2>
void mitk::DiffImageApplier::ItkImageProcessing3DDiff(itk::Image<TPixel1, VImageDimension1> *diffImage,
itk::Image<TPixel2, VImageDimension2> *outputImage)
{
typedef itk::Image<TPixel1, VImageDimension1> DiffImageType;
typedef itk::Image<TPixel2, VImageDimension2> VolumeImageType;
typedef itk::ImageRegionIterator<VolumeImageType> OutputSliceIteratorType;
typedef itk::ImageRegionConstIterator<DiffImageType> DiffSliceIteratorType;
OutputSliceIteratorType outputIterator(outputImage, outputImage->GetLargestPossibleRegion());
DiffSliceIteratorType diffIterator(diffImage, diffImage->GetLargestPossibleRegion());
// iterate over output slice (and over input slice simultaneously)
outputIterator.GoToBegin();
diffIterator.GoToBegin();
while (!outputIterator.IsAtEnd())
{
TPixel2 newValue = outputIterator.Get() + (TPixel2)((double)diffIterator.Get() * m_Factor);
outputIterator.Set(newValue);
++outputIterator;
++diffIterator;
}
}
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable:4146) // unary minus operator applied to unsigned type, result still unsigned
#endif
template <typename TPixel, unsigned int VImageDimension>
void mitk::DiffImageApplier::ItkInvertPixelValues(itk::Image<TPixel, VImageDimension> *itkImage)
{
typedef itk::ImageRegionIterator<itk::Image<TPixel, VImageDimension>> IteratorType;
IteratorType iter(itkImage, itkImage->GetLargestPossibleRegion());
iter.GoToBegin();
while (!iter.IsAtEnd())
{
iter.Set(-(iter.Get()));
++iter;
}
}
#ifdef _MSC_VER
# pragma warning(pop)
#endif
diff --git a/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.cpp b/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.cpp
index f9f57a08fb..5426cb0a21 100644
--- a/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.cpp
+++ b/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.cpp
@@ -1,290 +1,290 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkSurfaceStampImageFilter.h"
#include "mitkImageAccessByItk.h"
#include "mitkImageWriteAccessor.h"
#include "mitkTimeHelper.h"
#include <mitkImageCast.h>
#include <vtkCell.h>
#include <vtkLinearTransform.h>
#include <vtkPolyData.h>
#include <vtkSmartPointer.h>
#include <vtkTransform.h>
#include <vtkTransformPolyDataFilter.h>
#include <itkTriangleMeshToBinaryImageFilter.h>
mitk::SurfaceStampImageFilter::SurfaceStampImageFilter()
: m_MakeOutputBinary(false), m_OverwriteBackground(false), m_BackgroundValue(0.0), m_ForegroundValue(1.0)
{
}
mitk::SurfaceStampImageFilter::~SurfaceStampImageFilter()
{
}
void mitk::SurfaceStampImageFilter::GenerateInputRequestedRegion()
{
mitk::Image *outputImage = this->GetOutput();
if ((outputImage->IsInitialized() == false))
return;
GenerateTimeInInputRegion(outputImage, this->GetInput());
}
void mitk::SurfaceStampImageFilter::GenerateOutputInformation()
{
mitk::Image::ConstPointer inputImage = this->GetInput();
mitk::Image::Pointer outputImage = this->GetOutput();
itkDebugMacro(<< "GenerateOutputInformation()");
if (inputImage.IsNull() || (inputImage->IsInitialized() == false) || (inputImage->GetTimeGeometry() == nullptr))
return;
if (m_MakeOutputBinary)
outputImage->Initialize(mitk::MakeScalarPixelType<unsigned char>(), *inputImage->GetTimeGeometry());
else
outputImage->Initialize(inputImage->GetPixelType(), *inputImage->GetTimeGeometry());
outputImage->SetPropertyList(inputImage->GetPropertyList()->Clone());
}
void mitk::SurfaceStampImageFilter::SetSurface(mitk::Surface *surface)
{
m_Surface = surface;
}
void mitk::SurfaceStampImageFilter::GenerateData()
{
mitk::Image::ConstPointer inputImage = this->GetInput();
if (inputImage.IsNull())
return;
mitk::Image::Pointer outputImage = this->GetOutput();
if (outputImage->IsInitialized() == false)
return;
if (m_Surface.IsNull())
return;
mitk::Image::RegionType outputRegion = outputImage->GetRequestedRegion();
int tstart = outputRegion.GetIndex(3);
int tmax = tstart + outputRegion.GetSize(3);
if (tmax > 0)
{
int t;
for (t = tstart; t < tmax; ++t)
{
this->SurfaceStamp(t);
}
}
else
{
this->SurfaceStamp(0);
}
}
void mitk::SurfaceStampImageFilter::SurfaceStamp(int time)
{
mitk::Image::Pointer inputImage = this->GetInput();
const mitk::TimeGeometry *surfaceTimeGeometry = GetInput()->GetTimeGeometry();
const mitk::TimeGeometry *imageTimeGeometry = inputImage->GetTimeGeometry();
// Convert time step from image time-frame to surface time-frame
mitk::TimePointType matchingTimePoint = imageTimeGeometry->TimeStepToTimePoint(time);
mitk::TimeStepType surfaceTimeStep = surfaceTimeGeometry->TimePointToTimeStep(matchingTimePoint);
vtkPolyData *polydata = m_Surface->GetVtkPolyData(surfaceTimeStep);
if (!polydata)
mitkThrow() << "Polydata is null.";
vtkSmartPointer<vtkTransformPolyDataFilter> transformFilter = vtkSmartPointer<vtkTransformPolyDataFilter>::New();
transformFilter->SetInputData(polydata);
// transformFilter->ReleaseDataFlagOn();
vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
BaseGeometry::Pointer geometry = surfaceTimeGeometry->GetGeometryForTimeStep(surfaceTimeStep);
transform->PostMultiply();
transform->Concatenate(geometry->GetVtkTransform()->GetMatrix());
// take image geometry into account. vtk-Image information will be changed to unit spacing and zero origin below.
BaseGeometry::Pointer imageGeometry = imageTimeGeometry->GetGeometryForTimeStep(time);
transform->Concatenate(imageGeometry->GetVtkTransform()->GetLinearInverse());
transformFilter->SetTransform(transform);
transformFilter->Update();
polydata = transformFilter->GetOutput();
if (!polydata || !polydata->GetNumberOfPoints())
mitkThrow() << "Polydata retrieved from transformation is null or has no points.";
MeshType::Pointer mesh = MeshType::New();
- mesh->SetCellsAllocationMethod(MeshType::CellsAllocatedDynamicallyCellByCell);
+ mesh->SetCellsAllocationMethod(itk::MeshEnums::MeshClassCellsAllocationMethod::CellsAllocatedDynamicallyCellByCell);
unsigned int numberOfPoints = polydata->GetNumberOfPoints();
mesh->GetPoints()->Reserve(numberOfPoints);
vtkPoints *points = polydata->GetPoints();
MeshType::PointType point;
for (unsigned int i = 0; i < numberOfPoints; i++)
{
double *aux = points->GetPoint(i);
point[0] = aux[0];
point[1] = aux[1];
point[2] = aux[2];
mesh->SetPoint(i, point);
}
// Load the polygons into the itk::Mesh
typedef MeshType::CellAutoPointer CellAutoPointerType;
typedef MeshType::CellType CellType;
typedef itk::TriangleCell<CellType> TriangleCellType;
typedef MeshType::PointIdentifier PointIdentifierType;
typedef MeshType::CellIdentifier CellIdentifierType;
// Read the number of polygons
CellIdentifierType numberOfPolygons = 0;
numberOfPolygons = polydata->GetNumberOfPolys();
PointIdentifierType numberOfCellPoints = 3;
CellIdentifierType i = 0;
for (i = 0; i < numberOfPolygons; i++)
{
vtkIdList *cellIds;
vtkCell *vcell = polydata->GetCell(i);
cellIds = vcell->GetPointIds();
CellAutoPointerType cell;
auto *triangleCell = new TriangleCellType;
PointIdentifierType k;
for (k = 0; k < numberOfCellPoints; k++)
{
triangleCell->SetPointId(k, cellIds->GetId(k));
}
cell.TakeOwnership(triangleCell);
mesh->SetCell(i, cell);
}
if (!mesh->GetNumberOfPoints())
mitkThrow() << "Generated itk mesh is empty.";
if (m_MakeOutputBinary)
{
this->SurfaceStampBinaryOutputProcessing(mesh);
}
else
{
AccessFixedDimensionByItk_1(inputImage, SurfaceStampProcessing, 3, mesh);
}
}
void mitk::SurfaceStampImageFilter::SurfaceStampBinaryOutputProcessing(MeshType *mesh)
{
auto *inputImage = this->GetInput();
mitk::Image::Pointer outputImage = this->GetOutput();
typedef itk::Image<unsigned char, 3> BinaryImageType;
BinaryImageType::Pointer itkInputImage;
mitk::CastToItkImage(inputImage, itkInputImage);
typedef itk::TriangleMeshToBinaryImageFilter<MeshType, BinaryImageType> FilterType;
FilterType::Pointer filter = FilterType::New();
filter->SetInput(mesh);
filter->SetInfoImage(itkInputImage);
filter->SetInsideValue(1);
filter->SetOutsideValue(0);
filter->Update();
BinaryImageType::Pointer resultImage = filter->GetOutput();
resultImage->DisconnectPipeline();
mitk::CastToMitkImage(resultImage, outputImage);
}
template <typename TPixel>
void mitk::SurfaceStampImageFilter::SurfaceStampProcessing(itk::Image<TPixel, 3> *input, MeshType *mesh)
{
typedef itk::Image<TPixel, 3> ImageType;
typedef itk::Image<unsigned char, 3> BinaryImageType;
typedef itk::TriangleMeshToBinaryImageFilter<mitk::SurfaceStampImageFilter::MeshType, BinaryImageType> FilterType;
BinaryImageType::Pointer binaryInput = BinaryImageType::New();
binaryInput->SetSpacing(input->GetSpacing());
binaryInput->SetOrigin(input->GetOrigin());
binaryInput->SetDirection(input->GetDirection());
binaryInput->SetRegions(input->GetLargestPossibleRegion());
binaryInput->Allocate();
binaryInput->FillBuffer(0);
FilterType::Pointer filter = FilterType::New();
filter->SetInput(mesh);
filter->SetInfoImage(binaryInput);
filter->SetInsideValue(1);
filter->SetOutsideValue(0);
filter->Update();
BinaryImageType::Pointer resultImage = filter->GetOutput();
resultImage->DisconnectPipeline();
mitk::Image::Pointer outputImage = this->GetOutput();
typename ImageType::Pointer itkOutputImage;
mitk::CastToItkImage(outputImage, itkOutputImage);
typedef itk::ImageRegionConstIterator<BinaryImageType> BinaryIteratorType;
typedef itk::ImageRegionConstIterator<ImageType> InputIteratorType;
typedef itk::ImageRegionIterator<ImageType> OutputIteratorType;
BinaryIteratorType sourceIter(resultImage, resultImage->GetLargestPossibleRegion());
sourceIter.GoToBegin();
InputIteratorType inputIter(input, input->GetLargestPossibleRegion());
inputIter.GoToBegin();
OutputIteratorType outputIter(itkOutputImage, itkOutputImage->GetLargestPossibleRegion());
outputIter.GoToBegin();
typename ImageType::PixelType inputValue;
unsigned char sourceValue;
auto fgValue = static_cast<typename ImageType::PixelType>(m_ForegroundValue);
auto bgValue = static_cast<typename ImageType::PixelType>(m_BackgroundValue);
while (!sourceIter.IsAtEnd())
{
sourceValue = static_cast<unsigned char>(sourceIter.Get());
inputValue = static_cast<typename ImageType::PixelType>(inputIter.Get());
if (sourceValue != 0)
outputIter.Set(fgValue);
else if (m_OverwriteBackground)
outputIter.Set(bgValue);
else
outputIter.Set(inputValue);
++sourceIter;
++inputIter;
++outputIter;
}
}
diff --git a/Modules/Segmentation/CMakeLists.txt b/Modules/Segmentation/CMakeLists.txt
index 9c77de4682..865c34266e 100644
--- a/Modules/Segmentation/CMakeLists.txt
+++ b/Modules/Segmentation/CMakeLists.txt
@@ -1,9 +1,9 @@
mitk_create_module(
INCLUDE_DIRS Algorithms Controllers DataManagement Interactions Rendering SegmentationUtilities/BooleanOperations SegmentationUtilities/MorphologicalOperations
DEPENDS MitkAlgorithmsExt MitkSurfaceInterpolation MitkGraphAlgorithms MitkContourModel MitkMultilabel
PACKAGE_DEPENDS
- PUBLIC ITK|QuadEdgeMesh
+ PUBLIC ITK|QuadEdgeMesh+RegionGrowing
PRIVATE ITK|LabelMap+Watersheds VTK|ImagingGeneral
)
add_subdirectory(Testing)
diff --git a/Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp b/Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp
index 6af64c8130..1e4d843a0b 100644
--- a/Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp
+++ b/Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp
@@ -1,383 +1,382 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkExtrudedContour.h"
#include "mitkBaseProcess.h"
#include "mitkNumericTypes.h"
#include "mitkProportionalTimeGeometry.h"
#include <vtkCellArray.h>
#include <vtkClipPolyData.h>
#include <vtkLinearExtrusionFilter.h>
#include <vtkPlanes.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkDoubleArray.h>
#include <vtkFloatArray.h>
#include <vtkPlane.h>
#include <vtkPolygon.h>
// vtkButterflySubdivisionFilter * subdivs;
#include <vtkCubeSource.h>
#include <vtkImageData.h>
#include <vtkLinearSubdivisionFilter.h>
#include <vtkSampleFunction.h>
#include <vtkTriangleFilter.h>
mitk::ExtrudedContour::ExtrudedContour()
: m_Contour(nullptr), m_ClippingGeometry(nullptr), m_AutomaticVectorGeneration(false), m_Decimate(nullptr)
{
ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New();
timeGeometry->Initialize(1);
SetTimeGeometry(timeGeometry);
FillVector3D(m_Vector, 0.0, 0.0, 1.0);
m_RightVector.Fill(0.0);
m_ExtrusionFilter = vtkLinearExtrusionFilter::New();
m_ExtrusionFilter->CappingOff();
m_ExtrusionFilter->SetExtrusionTypeToVectorExtrusion();
double vtkvector[3] = {0, 0, 1};
// set extrusion vector
m_ExtrusionFilter->SetVector(vtkvector);
m_TriangleFilter = vtkTriangleFilter::New();
m_TriangleFilter->SetInputConnection(m_ExtrusionFilter->GetOutputPort());
m_SubdivisionFilter = vtkLinearSubdivisionFilter::New();
m_SubdivisionFilter->SetInputConnection(m_TriangleFilter->GetOutputPort());
m_SubdivisionFilter->SetNumberOfSubdivisions(4);
m_ClippingBox = vtkPlanes::New();
m_ClipPolyDataFilter = vtkClipPolyData::New();
m_ClipPolyDataFilter->SetInputConnection(m_SubdivisionFilter->GetOutputPort());
m_ClipPolyDataFilter->SetClipFunction(m_ClippingBox);
m_ClipPolyDataFilter->InsideOutOn();
m_Polygon = vtkPolygon::New();
m_ProjectionPlane = mitk::PlaneGeometry::New();
}
mitk::ExtrudedContour::~ExtrudedContour()
{
m_ClipPolyDataFilter->Delete();
m_ClippingBox->Delete();
m_SubdivisionFilter->Delete();
m_TriangleFilter->Delete();
m_ExtrusionFilter->Delete();
m_Polygon->Delete();
}
bool mitk::ExtrudedContour::IsInside(const Point3D &worldPoint) const
{
static double polygonNormal[3] = {0.0, 0.0, 1.0};
// project point onto plane
float xt[3];
itk2vtk(worldPoint, xt);
xt[0] = worldPoint[0] - m_Origin[0];
xt[1] = worldPoint[1] - m_Origin[1];
xt[2] = worldPoint[2] - m_Origin[2];
float dist = xt[0] * m_Normal[0] + xt[1] * m_Normal[1] + xt[2] * m_Normal[2];
xt[0] -= dist * m_Normal[0];
xt[1] -= dist * m_Normal[1];
xt[2] -= dist * m_Normal[2];
double x[3];
x[0] = xt[0] * m_Right[0] + xt[1] * m_Right[1] + xt[2] * m_Right[2];
x[1] = xt[0] * m_Down[0] + xt[1] * m_Down[1] + xt[2] * m_Down[2];
x[2] = 0;
// determine whether it's in the selection loop and then evaluate point
// in polygon only if absolutely necessary.
if (x[0] >= this->m_ProjectedContourBounds[0] && x[0] <= this->m_ProjectedContourBounds[1] &&
x[1] >= this->m_ProjectedContourBounds[2] && x[1] <= this->m_ProjectedContourBounds[3] &&
this->m_Polygon->PointInPolygon(x,
m_Polygon->Points->GetNumberOfPoints(),
((vtkDoubleArray *)this->m_Polygon->Points->GetData())->GetPointer(0),
(double *)this->m_ProjectedContourBounds,
polygonNormal) == 1)
return true;
else
return false;
}
mitk::ScalarType mitk::ExtrudedContour::GetVolume()
{
return -1.0;
}
void mitk::ExtrudedContour::UpdateOutputInformation()
{
if (this->GetSource())
{
this->GetSource()->UpdateOutputInformation();
}
if (GetMTime() > m_LastCalculateExtrusionTime)
{
BuildGeometry();
BuildSurface();
}
// if ( ( m_CalculateBoundingBox ) && ( m_PolyDataSeries.size() > 0 ) )
// CalculateBoundingBox();
}
void mitk::ExtrudedContour::BuildSurface()
{
if (m_Contour.IsNull())
{
SetVtkPolyData(nullptr);
return;
}
// set extrusion contour
vtkPolyData *polyData = vtkPolyData::New();
vtkCellArray *polys = vtkCellArray::New();
polys->InsertNextCell(m_Polygon->GetPointIds());
polyData->SetPoints(m_Polygon->GetPoints());
// float vtkpoint[3];
// unsigned int i, numPts = m_Polygon->GetNumberOfPoints();
// for(i=0; i<numPts; ++i)
//{
// float * vtkpoint = this->m_Polygon->Points->GetPoint(i);
// pointids[i]=loopPoints->InsertNextPoint(vtkpoint);
//}
// polys->InsertNextCell( i, pointids );
// delete [] pointids;
// polyData->SetPoints( loopPoints );
polyData->SetPolys(polys);
polys->Delete();
m_ExtrusionFilter->SetInputData(polyData);
polyData->Delete();
// set extrusion scale factor
m_ExtrusionFilter->SetScaleFactor(GetGeometry()->GetExtentInMM(2));
SetVtkPolyData(m_SubdivisionFilter->GetOutput());
// if(m_ClippingGeometry.IsNull())
//{
// SetVtkPolyData(m_SubdivisionFilter->GetOutput());
//}
// else
//{
// m_ClipPolyDataFilter->SetInput(m_SubdivisionFilter->GetOutput());
// mitk::BoundingBox::BoundsArrayType bounds=m_ClippingGeometry->GetBounds();
// m_ClippingBox->SetBounds(bounds[0], bounds[1], bounds[2], bounds[3], bounds[4], bounds[5]);
// m_ClippingBox->SetTransform(GetGeometry()->GetVtkTransform());
// m_ClipPolyDataFilter->SetClipFunction(m_ClippingBox);
// m_ClipPolyDataFilter->SetValue(0);
// SetVtkPolyData(m_ClipPolyDataFilter->GetOutput());
//}
m_LastCalculateExtrusionTime.Modified();
}
void mitk::ExtrudedContour::BuildGeometry()
{
if (m_Contour.IsNull())
return;
// Initialize(1);
Vector3D nullvector;
nullvector.Fill(0.0);
float xProj[3];
unsigned int i;
unsigned int numPts = 20; // m_Contour->GetNumberOfPoints();
mitk::Contour::PathPointer path = m_Contour->GetContourPath();
mitk::Contour::PathType::InputType cstart = path->StartOfInput();
mitk::Contour::PathType::InputType cend = path->EndOfInput();
mitk::Contour::PathType::InputType cstep = (cend - cstart) / numPts;
mitk::Contour::PathType::InputType ccur;
// Part I: guarantee/calculate legal vectors
m_Vector.Normalize();
itk2vtk(m_Vector, m_Normal);
// check m_Vector
if (mitk::Equal(m_Vector, nullvector) || m_AutomaticVectorGeneration)
{
if (m_AutomaticVectorGeneration == false)
itkWarningMacro("Extrusion vector is 0 (" << m_Vector << "); trying to use normal of polygon");
vtkPoints *loopPoints = vtkPoints::New();
// mitk::Contour::PointsContainerIterator pointsIt = m_Contour->GetPoints()->Begin();
double vtkpoint[3];
unsigned int i = 0;
for (i = 0, ccur = cstart; i < numPts; ++i, ccur += cstep)
{
itk2vtk(path->Evaluate(ccur), vtkpoint);
loopPoints->InsertNextPoint(vtkpoint);
}
// Make sure points define a loop with a m_Normal
vtkPolygon::ComputeNormal(loopPoints, m_Normal);
loopPoints->Delete();
vtk2itk(m_Normal, m_Vector);
if (mitk::Equal(m_Vector, nullvector))
{
itkExceptionMacro("Cannot calculate normal of polygon");
}
}
// check m_RightVector
if ((mitk::Equal(m_RightVector, nullvector)) || (mitk::Equal(m_RightVector * m_Vector, 0.0) == false))
{
if (mitk::Equal(m_RightVector, nullvector))
{
itkDebugMacro("Right vector is 0. Calculating.");
}
else
{
itkWarningMacro("Right vector (" << m_RightVector << ") not perpendicular to extrusion vector " << m_Vector
<< ": "
<< m_RightVector * m_Vector);
}
// calculate a legal m_RightVector
if (mitk::Equal(m_Vector[1], 0.0f) == false)
{
FillVector3D(m_RightVector, 1.0f, -m_Vector[0] / m_Vector[1], 0.0f);
m_RightVector.Normalize();
}
else
{
FillVector3D(m_RightVector, 0.0f, 1.0f, 0.0f);
}
}
// calculate down-vector
VnlVector rightDV = m_RightVector.GetVnlVector();
rightDV.normalize();
vnl2vtk(rightDV, m_Right);
VnlVector downDV = vnl_cross_3d(m_Vector.GetVnlVector(), rightDV);
downDV.normalize();
vnl2vtk(downDV, m_Down);
// Part II: calculate plane as base for extrusion, project the contour
// on this plane and store as polygon for IsInside test and BoundingBox calculation
// initialize m_ProjectionPlane, yet with origin at 0
m_ProjectionPlane->InitializeStandardPlane(rightDV, downDV);
// create vtkPolygon from contour and simultaneously determine 2D bounds of
// contour projected on m_ProjectionPlane
// mitk::Contour::PointsContainerIterator pointsIt = m_Contour->GetPoints()->Begin();
m_Polygon->Points->Reset();
m_Polygon->Points->SetNumberOfPoints(numPts);
m_Polygon->PointIds->Reset();
m_Polygon->PointIds->SetNumberOfIds(numPts);
mitk::Point2D pt2d;
mitk::Point3D pt3d;
mitk::Point2D min, max;
min.Fill(itk::NumericTraits<mitk::ScalarType>::max());
max.Fill(itk::NumericTraits<mitk::ScalarType>::min());
xProj[2] = 0.0;
for (i = 0, ccur = cstart; i < numPts; ++i, ccur += cstep)
{
pt3d.CastFrom(path->Evaluate(ccur));
m_ProjectionPlane->Map(pt3d, pt2d);
xProj[0] = pt2d[0];
if (pt2d[0] < min[0])
min[0] = pt2d[0];
if (pt2d[0] > max[0])
max[0] = pt2d[0];
xProj[1] = pt2d[1];
if (pt2d[1] < min[1])
min[1] = pt2d[1];
if (pt2d[1] > max[1])
max[1] = pt2d[1];
m_Polygon->Points->SetPoint(i, xProj);
m_Polygon->PointIds->SetId(i, i);
}
// shift parametric origin to (0,0)
for (i = 0; i < numPts; ++i)
{
double *pt = this->m_Polygon->Points->GetPoint(i);
pt[0] -= min[0];
pt[1] -= min[1];
itkDebugMacro(<< i << ": (" << pt[0] << "," << pt[1] << "," << pt[2] << ")");
}
this->m_Polygon->GetBounds(m_ProjectedContourBounds);
// m_ProjectedContourBounds[4]=-1.0; m_ProjectedContourBounds[5]=1.0;
// calculate origin (except translation along the normal) and bounds
// of m_ProjectionPlane:
// origin is composed of the minimum x-/y-coordinates of the polygon,
// bounds from the extent of the polygon, both after projecting on the plane
mitk::Point3D origin;
m_ProjectionPlane->Map(min, origin);
ScalarType bounds[6] = {0, max[0] - min[0], 0, max[1] - min[1], 0, 1};
m_ProjectionPlane->SetBounds(bounds);
m_ProjectionPlane->SetOrigin(origin);
// Part III: initialize geometry
if (m_ClippingGeometry.IsNotNull())
{
ScalarType min_dist = itk::NumericTraits<mitk::ScalarType>::max(),
max_dist = itk::NumericTraits<mitk::ScalarType>::min(), dist;
unsigned char i;
for (i = 0; i < 8; ++i)
{
dist = m_ProjectionPlane->SignedDistance(m_ClippingGeometry->GetCornerPoint(i));
if (dist < min_dist)
min_dist = dist;
if (dist > max_dist)
max_dist = dist;
}
// incorporate translation along the normal into origin
origin = origin + m_Vector * min_dist;
m_ProjectionPlane->SetOrigin(origin);
bounds[5] = max_dist - min_dist;
}
else
bounds[5] = 20;
itk2vtk(origin, m_Origin);
mitk::BaseGeometry::Pointer g3d = GetGeometry(0);
assert(g3d.IsNotNull());
g3d->SetBounds(bounds);
g3d->SetIndexToWorldTransform(m_ProjectionPlane->GetIndexToWorldTransform());
ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New();
timeGeometry->Initialize(g3d, 1);
SetTimeGeometry(timeGeometry);
}
-unsigned long mitk::ExtrudedContour::GetMTime() const
+itk::ModifiedTimeType mitk::ExtrudedContour::GetMTime() const
{
- unsigned long latestTime = Superclass::GetMTime();
+ auto latestTime = Superclass::GetMTime();
if (m_Contour.IsNotNull())
{
- unsigned long localTime;
- localTime = m_Contour->GetMTime();
+ auto localTime = m_Contour->GetMTime();
if (localTime > latestTime)
latestTime = localTime;
}
return latestTime;
}
diff --git a/Modules/Segmentation/DataManagement/mitkExtrudedContour.h b/Modules/Segmentation/DataManagement/mitkExtrudedContour.h
index 0d50912071..767a870412 100644
--- a/Modules/Segmentation/DataManagement/mitkExtrudedContour.h
+++ b/Modules/Segmentation/DataManagement/mitkExtrudedContour.h
@@ -1,127 +1,126 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKEXTRUDEDCONTOUR_H_HEADER_INCLUDED
#define MITKEXTRUDEDCONTOUR_H_HEADER_INCLUDED
#include "mitkBoundingObject.h"
#include <MitkSegmentationExports.h>
#include <mitkContour.h>
#include <mitkPlaneGeometry.h>
-#include <vtkConfigure.h>
#include <vtkVersionMacros.h>
class vtkLinearExtrusionFilter;
class vtkPlanes;
class vtkClipPolyData;
class vtkLinearSubdivisionFilter;
class vtkTriangleFilter;
class vtkDecimatePro;
class vtkPolygon;
namespace mitk
{
//##Documentation
//## @brief Data class containing a bounding-object created by
//## extruding a Contour along a vector
//##
//## The m_Contour is extruded in the direction m_Vector until
//## reaching m_ClippingGeometry.
//## @ingroup Data
/**
* \deprecatedSince{2015_05} ExtrudedContour is deprecated. It will be removed in the next release.
* Becomes obsolete. Refer to http://docs.mitk.org/nightly/InteractionMigration.html .
*/
class MITKSEGMENTATION_EXPORT ExtrudedContour : public BoundingObject
{
public:
mitkClassMacro(ExtrudedContour, BoundingObject);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
mitk::ScalarType GetVolume() override;
bool IsInside(const Point3D &p) const override;
void UpdateOutputInformation() override;
//##Documentation
//## @brief Contour to extrude
itkGetConstObjectMacro(Contour, mitk::Contour);
itkSetObjectMacro(Contour, mitk::Contour);
//##Documentation
//## @brief Vector to specify the direction of the extrusion
mitkGetVectorMacro(Vector, mitk::Vector3D);
mitkSetVectorMacro(Vector, mitk::Vector3D);
itkGetConstMacro(AutomaticVectorGeneration, bool);
itkSetMacro(AutomaticVectorGeneration, bool);
itkBooleanMacro(AutomaticVectorGeneration);
//##Documentation
//## @brief Optional vector to specify the orientation of the bounding-box
mitkGetVectorMacro(RightVector, mitk::Vector3D);
mitkSetVectorMacro(RightVector, mitk::Vector3D);
//##Documentation
//## @brief Optional geometry for clipping the extruded contour
itkGetConstObjectMacro(ClippingGeometry, mitk::BaseGeometry);
itkSetObjectMacro(ClippingGeometry, mitk::BaseGeometry);
- unsigned long GetMTime() const override;
+ itk::ModifiedTimeType GetMTime() const override;
protected:
ExtrudedContour();
~ExtrudedContour() override;
void BuildSurface();
void BuildGeometry();
mitk::Contour::Pointer m_Contour;
mitk::Vector3D m_Vector;
mitk::Vector3D m_RightVector;
mitk::BaseGeometry::Pointer m_ClippingGeometry;
bool m_AutomaticVectorGeneration;
vtkPolygon *m_Polygon;
#if ((VTK_MAJOR_VERSION > 4) || ((VTK_MAJOR_VERSION == 4) && (VTK_MINOR_VERSION >= 4)))
double m_ProjectedContourBounds[6];
#else
float m_ProjectedContourBounds[6];
#endif
mitk::PlaneGeometry::Pointer m_ProjectionPlane;
//##Documentation
//## @brief For fast projection on plane
float m_Right[3];
float m_Down[3];
#if ((VTK_MAJOR_VERSION > 4) || ((VTK_MAJOR_VERSION == 4) && (VTK_MINOR_VERSION >= 4)))
double m_Normal[3];
#else
float m_Normal[3];
#endif
float m_Origin[3];
vtkLinearExtrusionFilter *m_ExtrusionFilter;
vtkTriangleFilter *m_TriangleFilter;
vtkDecimatePro *m_Decimate;
vtkLinearSubdivisionFilter *m_SubdivisionFilter;
vtkPlanes *m_ClippingBox;
vtkClipPolyData *m_ClipPolyDataFilter;
itk::TimeStamp m_LastCalculateExtrusionTime;
};
}
#endif /* MITKEXTRUDEDCONTOUR_H_HEADER_INCLUDED */
diff --git a/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.cpp b/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.cpp
index 1f5dc1032e..cbc5a650cb 100644
--- a/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.cpp
+++ b/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.cpp
@@ -1,122 +1,122 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkBinaryThresholdBaseTool.h"
#include "mitkImageAccessByItk.h"
#include "mitkImageCast.h"
#include "mitkImageStatisticsHolder.h"
#include "mitkLabelSetImage.h"
#include <itkBinaryThresholdImageFilter.h>
#include <itkImageRegionIterator.h>
mitk::BinaryThresholdBaseTool::BinaryThresholdBaseTool()
: m_SensibleMinimumThreshold(-100),
m_SensibleMaximumThreshold(+100),
m_LowerThreshold(1),
m_UpperThreshold(1)
{
}
mitk::BinaryThresholdBaseTool::~BinaryThresholdBaseTool()
{
}
void mitk::BinaryThresholdBaseTool::SetThresholdValues(double lower, double upper)
{
/* If value is not in the min/max range, do nothing. In that case, this
method will be called again with a proper value right after. The only
known case where this happens is with an [0.0, 1.0[ image, where value
could be an epsilon greater than the max. */
if (lower < m_SensibleMinimumThreshold
|| lower > m_SensibleMaximumThreshold
|| upper < m_SensibleMinimumThreshold
|| upper > m_SensibleMaximumThreshold)
{
return;
}
m_LowerThreshold = lower;
m_UpperThreshold = upper;
if (nullptr != this->GetPreviewSegmentation())
{
UpdatePreview();
}
}
void mitk::BinaryThresholdBaseTool::InitiateToolByInput()
{
const auto referenceImage = this->GetReferenceData();
if (nullptr != referenceImage)
{
m_SensibleMinimumThreshold = std::numeric_limits<ScalarType>::max();
m_SensibleMaximumThreshold = std::numeric_limits<ScalarType>::lowest();
Image::StatisticsHolderPointer statistics = referenceImage->GetStatistics();
for (unsigned int ts = 0; ts < referenceImage->GetTimeSteps(); ++ts)
{
m_SensibleMinimumThreshold = std::min(m_SensibleMinimumThreshold, static_cast<double>(statistics->GetScalarValueMin()));
m_SensibleMaximumThreshold = std::max(m_SensibleMaximumThreshold, static_cast<double>(statistics->GetScalarValueMax()));
}
if (m_LockedUpperThreshold)
{
m_LowerThreshold = (m_SensibleMaximumThreshold + m_SensibleMinimumThreshold) / 2.0;
m_UpperThreshold = m_SensibleMaximumThreshold;
}
else
{
double range = m_SensibleMaximumThreshold - m_SensibleMinimumThreshold;
m_LowerThreshold = m_SensibleMinimumThreshold + range / 3.0;
m_UpperThreshold = m_SensibleMinimumThreshold + 2 * range / 3.0;
}
bool isFloatImage = false;
- if ((referenceImage->GetPixelType().GetPixelType() == itk::ImageIOBase::SCALAR) &&
- (referenceImage->GetPixelType().GetComponentType() == itk::ImageIOBase::FLOAT ||
- referenceImage->GetPixelType().GetComponentType() == itk::ImageIOBase::DOUBLE))
+ if ((referenceImage->GetPixelType().GetPixelType() == itk::IOPixelEnum::SCALAR) &&
+ (referenceImage->GetPixelType().GetComponentType() == itk::IOComponentEnum::FLOAT ||
+ referenceImage->GetPixelType().GetComponentType() == itk::IOComponentEnum::DOUBLE))
{
isFloatImage = true;
}
IntervalBordersChanged.Send(m_SensibleMinimumThreshold, m_SensibleMaximumThreshold, isFloatImage);
ThresholdingValuesChanged.Send(m_LowerThreshold, m_UpperThreshold);
}
}
void mitk::BinaryThresholdBaseTool::DoUpdatePreview(const Image* inputAtTimeStep, const Image* /*oldSegAtTimeStep*/, Image* previewImage, TimeStepType timeStep)
{
if (nullptr != inputAtTimeStep && nullptr != previewImage)
{
AccessByItk_n(inputAtTimeStep, ITKThresholding, (previewImage, timeStep));
}
}
template <typename TPixel, unsigned int VImageDimension>
void mitk::BinaryThresholdBaseTool::ITKThresholding(const itk::Image<TPixel, VImageDimension>* inputImage,
Image* segmentation,
unsigned int timeStep)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::Image<Tool::DefaultSegmentationDataType, VImageDimension> SegmentationType;
typedef itk::BinaryThresholdImageFilter<ImageType, SegmentationType> ThresholdFilterType;
typename ThresholdFilterType::Pointer filter = ThresholdFilterType::New();
filter->SetInput(inputImage);
filter->SetLowerThreshold(m_LowerThreshold);
filter->SetUpperThreshold(m_UpperThreshold);
filter->SetInsideValue(this->GetUserDefinedActiveLabel());
filter->SetOutsideValue(0);
filter->Update();
segmentation->SetVolume((void *)(filter->GetOutput()->GetPixelContainer()->GetBufferPointer()), timeStep);
}
diff --git a/Modules/Segmentation/Interactions/mitkFastMarchingTool.cpp b/Modules/Segmentation/Interactions/mitkFastMarchingTool.cpp
index 95eb57c850..c4a58520e0 100644
--- a/Modules/Segmentation/Interactions/mitkFastMarchingTool.cpp
+++ b/Modules/Segmentation/Interactions/mitkFastMarchingTool.cpp
@@ -1,34 +1,34 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkFastMarchingTool.h"
namespace mitk
{
- MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, FastMarchingTool, "FastMarching2D tool");
+ MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, FastMarchingTool, "FastMarching tool");
}
mitk::FastMarchingTool::FastMarchingTool()
: FastMarchingBaseTool(2)
{
}
mitk::FastMarchingTool::~FastMarchingTool()
{
}
const char *mitk::FastMarchingTool::GetName() const
{
- return "2D Fast Marching";
+ return "Fast Marching";
}
diff --git a/Modules/Segmentation/Interactions/mitkFeedbackContourTool.cpp b/Modules/Segmentation/Interactions/mitkFeedbackContourTool.cpp
index ba1be71de4..0015405496 100644
--- a/Modules/Segmentation/Interactions/mitkFeedbackContourTool.cpp
+++ b/Modules/Segmentation/Interactions/mitkFeedbackContourTool.cpp
@@ -1,272 +1,272 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkFeedbackContourTool.h"
#include "mitkToolManager.h"
#include "mitkColorProperty.h"
#include "mitkProperties.h"
#include "mitkStringProperty.h"
#include "mitkBaseRenderer.h"
#include "mitkDataStorage.h"
#include "mitkRenderingManager.h"
#include "mitkAbstractTransformGeometry.h"
mitk::FeedbackContourTool::FeedbackContourTool(const char *type) : SegTool2D(type), m_FeedbackContourVisible(false)
{
m_FeedbackContourNode = DataNode::New();
m_FeedbackContourNode->SetProperty("name", StringProperty::New("One of FeedbackContourTool's feedback nodes"));
m_FeedbackContourNode->SetProperty("visible", BoolProperty::New(true));
m_FeedbackContourNode->SetProperty("helper object", BoolProperty::New(true));
m_FeedbackContourNode->SetProperty("layer", IntProperty::New(1000));
m_FeedbackContourNode->SetProperty("contour.project-onto-plane", BoolProperty::New(false));
m_FeedbackContourNode->SetProperty("contour.width", FloatProperty::New(1.0));
// set to max short, max int doesn't work, max value somewhere hardcoded?
m_FeedbackContourNode->SetProperty("layer", IntProperty::New(std::numeric_limits<short>::max()));
m_FeedbackContourNode->SetProperty("fixedLayer", BoolProperty::New(true));
this->InitializeFeedbackContour(true);
SetFeedbackContourColorDefault();
}
mitk::FeedbackContourTool::~FeedbackContourTool()
{
}
void mitk::FeedbackContourTool::SetFeedbackContourColor(float r, float g, float b)
{
m_FeedbackContourNode->SetProperty("contour.color", ColorProperty::New(r, g, b));
}
void mitk::FeedbackContourTool::SetFeedbackContourColorDefault()
{
- m_FeedbackContourNode->SetProperty("contour.color", ColorProperty::New(0.0 / 255.0, 255.0 / 255.0, 0.0 / 255.0));
+ this->SetFeedbackContourColor(0.0, 1.0, 0.0);
}
void mitk::FeedbackContourTool::Deactivated()
{
Superclass::Deactivated();
DataStorage *storage = this->GetToolManager()->GetDataStorage();
if (storage && m_FeedbackContourNode.IsNotNull())
{
storage->Remove(m_FeedbackContourNode);
m_FeedbackContour->Clear();
SetFeedbackContourVisible(false);
}
}
void mitk::FeedbackContourTool::Activated()
{
Superclass::Activated();
this->InitializeFeedbackContour(true);
this->SetFeedbackContourVisible(true);
}
const mitk::ContourModel *mitk::FeedbackContourTool::GetFeedbackContour() const
{
return m_FeedbackContour;
}
void mitk::FeedbackContourTool::InitializeFeedbackContour(bool isClosed)
{
m_FeedbackContour = ContourModel::New();
m_FeedbackContour->SetClosed(isClosed);
auto workingImage = this->GetWorkingData();
if (nullptr != workingImage)
{
m_FeedbackContour->Expand(workingImage->GetTimeSteps());
auto contourTimeGeometry = workingImage->GetTimeGeometry()->Clone();
contourTimeGeometry->ReplaceTimeStepGeometries(m_FeedbackContour->GetGeometry());
m_FeedbackContour->SetTimeGeometry(contourTimeGeometry);
for (unsigned int t = 0; t < m_FeedbackContour->GetTimeSteps(); ++t)
{
m_FeedbackContour->SetClosed(isClosed, t);
}
}
m_FeedbackContourNode->SetData(m_FeedbackContour);
}
void mitk::FeedbackContourTool::ClearsCurrentFeedbackContour(bool isClosed)
{
if (!m_FeedbackContour->GetTimeGeometry()->IsValidTimePoint(this->GetLastTimePointTriggered()))
{
MITK_WARN << "Cannot clear feedback contour at current time step. Feedback contour is in invalid state as its time geometry does not support current selected time point. Invalid time point: " << this->GetLastTimePointTriggered();
return;
}
auto feedbackTimeStep = m_FeedbackContour->GetTimeGeometry()->TimePointToTimeStep(this->GetLastTimePointTriggered());
m_FeedbackContour->Clear(feedbackTimeStep);
m_FeedbackContour->SetClosed(isClosed, feedbackTimeStep);
}
void mitk::FeedbackContourTool::UpdateCurrentFeedbackContour(const ContourModel* sourceModel, TimeStepType sourceTimeStep)
{
if (nullptr == sourceModel)
return;
if (!m_FeedbackContour->GetTimeGeometry()->IsValidTimePoint(this->GetLastTimePointTriggered()))
{
MITK_WARN << "Cannot update feedback contour. Feedback contour is in invalid state as its time geometry does not support current selected time point. Invalid time point: "<<this->GetLastTimePointTriggered();
return;
}
auto feedbackTimeStep = m_FeedbackContour->GetTimeGeometry()->TimePointToTimeStep(this->GetLastTimePointTriggered());
this->UpdateFeedbackContour(sourceModel, feedbackTimeStep, sourceTimeStep);
}
void mitk::FeedbackContourTool::UpdateFeedbackContour(const ContourModel* sourceModel, TimeStepType feedbackTimeStep, TimeStepType sourceTimeStep)
{
if (nullptr == sourceModel)
return;
if (!sourceModel->GetTimeGeometry()->IsValidTimeStep(sourceTimeStep))
{
MITK_WARN << "Cannot update feedback contour. Source contour time geometry does not support passed time step. Invalid time step: " << sourceTimeStep;
return;
}
if (!m_FeedbackContour->GetTimeGeometry()->IsValidTimeStep(feedbackTimeStep))
{
MITK_WARN << "Cannot update feedback contour. Feedback contour time geometry does not support passed time step. Invalid time step: "<<feedbackTimeStep;
return;
}
m_FeedbackContour->UpdateContour(sourceModel, feedbackTimeStep, sourceTimeStep);
}
void mitk::FeedbackContourTool::AddVertexToCurrentFeedbackContour(const Point3D& point)
{
if (!m_FeedbackContour->GetTimeGeometry()->IsValidTimePoint(this->GetLastTimePointTriggered()))
{
MITK_WARN << "Cannot add vertex to feedback contour. Feedback contour is in invalid state as its time geometry does not support current selected time point. Invalid time point: " << this->GetLastTimePointTriggered();
return;
}
auto feedbackTimeStep = m_FeedbackContour->GetTimeGeometry()->TimePointToTimeStep(this->GetLastTimePointTriggered());
this->AddVertexToFeedbackContour(point, feedbackTimeStep);
};
/** Adds a vertex to the feedback contour for the passed time step. If time step is invalid, nothing will be added.*/
void mitk::FeedbackContourTool::AddVertexToFeedbackContour(const Point3D& point, TimeStepType feedbackTimeStep)
{
if (!m_FeedbackContour->GetTimeGeometry()->IsValidTimeStep(feedbackTimeStep))
{
MITK_WARN << "Cannot add vertex to feedback contour. Feedback contour time geometry does not support passed time step. Invalid time step: " << feedbackTimeStep;
return;
}
m_FeedbackContour->AddVertex(point, feedbackTimeStep);
}
void mitk::FeedbackContourTool::SetFeedbackContourVisible(bool visible)
{
if (m_FeedbackContourVisible == visible)
return; // nothing changed
if (DataStorage *storage = this->GetToolManager()->GetDataStorage())
{
if (visible)
{
// Add the feedback contour node as a derived node of the first working data.
// If there is no working data, the node is added at the top level.
storage->Add(m_FeedbackContourNode, this->GetWorkingDataNode());
}
else
{
storage->Remove(m_FeedbackContourNode);
}
}
m_FeedbackContourVisible = visible;
}
mitk::ContourModel::Pointer mitk::FeedbackContourTool::ProjectContourTo2DSlice(const Image *slice,
const ContourModel *contourIn3D)
{
return mitk::ContourModelUtils::ProjectContourTo2DSlice(slice, contourIn3D);
}
mitk::ContourModel::Pointer mitk::FeedbackContourTool::BackProjectContourFrom2DSlice(const BaseGeometry *sliceGeometry,
const ContourModel *contourIn2D)
{
return mitk::ContourModelUtils::BackProjectContourFrom2DSlice(sliceGeometry, contourIn2D);
}
void mitk::FeedbackContourTool::WriteBackFeedbackContourAsSegmentationResult(const InteractionPositionEvent* positionEvent, int paintingPixelValue, bool setInvisibleAfterSuccess)
{
if (!positionEvent)
return;
auto workingImage = this->GetWorkingData();
const auto planeGeometry((positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()));
if (!workingImage || !planeGeometry)
return;
const auto* abstractTransformGeometry(
dynamic_cast<const AbstractTransformGeometry*>(positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()));
if (nullptr != abstractTransformGeometry)
return;
Image::Pointer slice = FeedbackContourTool::GetAffectedWorkingSlice(positionEvent);
if (slice.IsNull())
{
MITK_ERROR << "Unable to extract slice." << std::endl;
return;
}
const auto feedbackContour = FeedbackContourTool::GetFeedbackContour();
auto contourTimeStep = positionEvent->GetSender()->GetTimeStep(feedbackContour);
ContourModel::Pointer projectedContour = FeedbackContourTool::ProjectContourTo2DSlice(
slice, feedbackContour);
if (projectedContour.IsNull())
return;
auto activePixelValue = ContourModelUtils::GetActivePixelValue(workingImage);
mitk::ContourModelUtils::FillContourInSlice(
projectedContour, contourTimeStep, slice, workingImage, paintingPixelValue * activePixelValue);
this->WriteBackSegmentationResult(positionEvent, slice);
if (setInvisibleAfterSuccess)
{
this->SetFeedbackContourVisible(false);
}
}
void mitk::FeedbackContourTool::FillContourInSlice(ContourModel *projectedContour,
Image *sliceImage,
int paintingPixelValue)
{
this->FillContourInSlice(projectedContour, 0, sliceImage, paintingPixelValue);
}
void mitk::FeedbackContourTool::FillContourInSlice(ContourModel *projectedContour,
unsigned int timeStep,
Image *sliceImage,
int paintingPixelValue)
{
mitk::ContourModelUtils::FillContourInSlice(projectedContour, timeStep, sliceImage, sliceImage, paintingPixelValue);
}
diff --git a/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp b/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp
index 080fb76ab2..1482e9018c 100644
--- a/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp
+++ b/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp
@@ -1,566 +1,579 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkPaintbrushTool.h"
#include "mitkAbstractTransformGeometry.h"
#include "mitkBaseRenderer.h"
#include "mitkToolManager.h"
#include "mitkContourModelUtils.h"
#include "mitkLevelWindowProperty.h"
int mitk::PaintbrushTool::m_Size = 1;
mitk::PaintbrushTool::PaintbrushTool(int paintingPixelValue)
: FeedbackContourTool("PressMoveReleaseWithCTRLInversionAllMouseMoves"),
m_PaintingPixelValue(paintingPixelValue),
m_LastContourSize(0) // other than initial mitk::PaintbrushTool::m_Size (around l. 28)
{
m_MasterContour = ContourModel::New();
m_MasterContour->Initialize();
m_CurrentPlane = nullptr;
m_WorkingNode = DataNode::New();
m_WorkingNode->SetProperty("levelwindow", mitk::LevelWindowProperty::New(mitk::LevelWindow(0, 1)));
m_WorkingNode->SetProperty("binary", mitk::BoolProperty::New(true));
}
mitk::PaintbrushTool::~PaintbrushTool()
{
}
void mitk::PaintbrushTool::ConnectActionsAndFunctions()
{
CONNECT_FUNCTION("PrimaryButtonPressed", OnMousePressed);
CONNECT_FUNCTION("Move", OnPrimaryButtonPressedMoved);
CONNECT_FUNCTION("MouseMove", OnMouseMoved);
CONNECT_FUNCTION("Release", OnMouseReleased);
CONNECT_FUNCTION("InvertLogic", OnInvertLogic);
}
void mitk::PaintbrushTool::Activated()
{
Superclass::Activated();
FeedbackContourTool::SetFeedbackContourVisible(true);
SizeChanged.Send(m_Size);
this->GetToolManager()->WorkingDataChanged +=
mitk::MessageDelegate<mitk::PaintbrushTool>(this, &mitk::PaintbrushTool::OnToolManagerWorkingDataModified);
}
void mitk::PaintbrushTool::Deactivated()
{
FeedbackContourTool::SetFeedbackContourVisible(false);
if (this->GetToolManager()->GetDataStorage()->Exists(m_WorkingNode))
this->GetToolManager()->GetDataStorage()->Remove(m_WorkingNode);
m_WorkingSlice = nullptr;
m_CurrentPlane = nullptr;
this->GetToolManager()->WorkingDataChanged -=
mitk::MessageDelegate<mitk::PaintbrushTool>(this, &mitk::PaintbrushTool::OnToolManagerWorkingDataModified);
Superclass::Deactivated();
}
void mitk::PaintbrushTool::SetSize(int value)
{
m_Size = value;
}
mitk::Point2D mitk::PaintbrushTool::upperLeft(mitk::Point2D p)
{
p[0] -= 0.5;
p[1] += 0.5;
return p;
}
void mitk::PaintbrushTool::UpdateContour(const InteractionPositionEvent *positionEvent)
{
// MITK_INFO<<"Update...";
// examine stateEvent and create a contour that matches the pixel mask that we are going to draw
// mitk::InteractionPositionEvent* positionEvent = dynamic_cast<mitk::InteractionPositionEvent*>( interactionEvent );
// const PositionEvent* positionEvent = dynamic_cast<const PositionEvent*>(stateEvent->GetEvent());
if (!positionEvent)
return;
// Get Spacing of current Slice
// mitk::Vector3D vSpacing = m_WorkingSlice->GetSlicedGeometry()->GetPlaneGeometry(0)->GetSpacing();
//
// Draw a contour in Square according to selected brush size
//
int radius = (m_Size) / 2;
float fradius = static_cast<float>(m_Size) / 2.0f;
ContourModel::Pointer contourInImageIndexCoordinates = ContourModel::New();
// estimate center point of the brush ( relative to the pixel the mouse points on )
// -- left upper corner for even sizes,
// -- midpoint for uneven sizes
mitk::Point2D centerCorrection;
centerCorrection.Fill(0);
// even --> correction of [+0.5, +0.5]
bool evenSize = ((m_Size % 2) == 0);
if (evenSize)
{
centerCorrection[0] += 0.5;
centerCorrection[1] += 0.5;
}
// we will compute the control points for the upper left quarter part of a circle contour
std::vector<mitk::Point2D> quarterCycleUpperRight;
std::vector<mitk::Point2D> quarterCycleLowerRight;
std::vector<mitk::Point2D> quarterCycleLowerLeft;
std::vector<mitk::Point2D> quarterCycleUpperLeft;
mitk::Point2D curPoint;
bool curPointIsInside = true;
curPoint[0] = 0;
curPoint[1] = radius;
quarterCycleUpperRight.push_back(upperLeft(curPoint));
// to estimate if a pixel is inside the circle, we need to compare against the 'outer radius'
// i.e. the distance from the midpoint [0,0] to the border of the pixel [0,radius]
// const float outer_radius = static_cast<float>(radius) + 0.5;
while (curPoint[1] > 0)
{
// Move right until pixel is outside circle
float curPointX_squared = 0.0f;
float curPointY_squared = (curPoint[1] - centerCorrection[1]) * (curPoint[1] - centerCorrection[1]);
while (curPointIsInside)
{
// increment posX and chec
curPoint[0]++;
curPointX_squared = (curPoint[0] - centerCorrection[0]) * (curPoint[0] - centerCorrection[0]);
const float len = sqrt(curPointX_squared + curPointY_squared);
if (len > fradius)
{
// found first Pixel in this horizontal line, that is outside the circle
curPointIsInside = false;
}
}
quarterCycleUpperRight.push_back(upperLeft(curPoint));
// Move down until pixel is inside circle
while (!curPointIsInside)
{
// increment posX and chec
curPoint[1]--;
curPointY_squared = (curPoint[1] - centerCorrection[1]) * (curPoint[1] - centerCorrection[1]);
const float len = sqrt(curPointX_squared + curPointY_squared);
if (len <= fradius)
{
// found first Pixel in this horizontal line, that is outside the circle
curPointIsInside = true;
quarterCycleUpperRight.push_back(upperLeft(curPoint));
}
// Quarter cycle is full, when curPoint y position is 0
if (curPoint[1] <= 0)
break;
}
}
// QuarterCycle is full! Now copy quarter cycle to other quarters.
if (!evenSize)
{
std::vector<mitk::Point2D>::const_iterator it = quarterCycleUpperRight.begin();
while (it != quarterCycleUpperRight.end())
{
mitk::Point2D p;
p = *it;
// the contour points in the lower right corner have same position but with negative y values
p[1] *= -1;
quarterCycleLowerRight.push_back(p);
// the contour points in the lower left corner have same position
// but with both x,y negative
p[0] *= -1;
quarterCycleLowerLeft.push_back(p);
// the contour points in the upper left corner have same position
// but with x negative
p[1] *= -1;
quarterCycleUpperLeft.push_back(p);
it++;
}
}
else
{
std::vector<mitk::Point2D>::const_iterator it = quarterCycleUpperRight.begin();
while (it != quarterCycleUpperRight.end())
{
mitk::Point2D p, q;
p = *it;
q = p;
// the contour points in the lower right corner have same position but with negative y values
q[1] *= -1;
// correct for moved offset if size even = the midpoint is not the midpoint of the current pixel
// but its upper rigt corner
q[1] += 1;
quarterCycleLowerRight.push_back(q);
q = p;
// the contour points in the lower left corner have same position
// but with both x,y negative
q[1] = -1.0f * q[1] + 1;
q[0] = -1.0f * q[0] + 1;
quarterCycleLowerLeft.push_back(q);
// the contour points in the upper left corner have same position
// but with x negative
q = p;
q[0] *= -1;
q[0] += 1;
quarterCycleUpperLeft.push_back(q);
it++;
}
}
// fill contour with poins in right ordering, starting with the upperRight block
mitk::Point3D tempPoint;
for (unsigned int i = 0; i < quarterCycleUpperRight.size(); i++)
{
tempPoint[0] = quarterCycleUpperRight[i][0];
tempPoint[1] = quarterCycleUpperRight[i][1];
tempPoint[2] = 0;
contourInImageIndexCoordinates->AddVertex(tempPoint);
}
// the lower right has to be parsed in reverse order
for (int i = quarterCycleLowerRight.size() - 1; i >= 0; i--)
{
tempPoint[0] = quarterCycleLowerRight[i][0];
tempPoint[1] = quarterCycleLowerRight[i][1];
tempPoint[2] = 0;
contourInImageIndexCoordinates->AddVertex(tempPoint);
}
for (unsigned int i = 0; i < quarterCycleLowerLeft.size(); i++)
{
tempPoint[0] = quarterCycleLowerLeft[i][0];
tempPoint[1] = quarterCycleLowerLeft[i][1];
tempPoint[2] = 0;
contourInImageIndexCoordinates->AddVertex(tempPoint);
}
// the upper left also has to be parsed in reverse order
for (int i = quarterCycleUpperLeft.size() - 1; i >= 0; i--)
{
tempPoint[0] = quarterCycleUpperLeft[i][0];
tempPoint[1] = quarterCycleUpperLeft[i][1];
tempPoint[2] = 0;
contourInImageIndexCoordinates->AddVertex(tempPoint);
}
m_MasterContour = contourInImageIndexCoordinates;
}
/**
Just show the contour, get one point as the central point and add surrounding points to the contour.
*/
void mitk::PaintbrushTool::OnMousePressed(StateMachineAction *, InteractionEvent *interactionEvent)
{
if (m_WorkingSlice.IsNull())
return;
auto *positionEvent = dynamic_cast<mitk::InteractionPositionEvent *>(interactionEvent);
if (!positionEvent)
return;
m_WorkingSlice->GetGeometry()->WorldToIndex(positionEvent->GetPositionInWorld(), m_LastPosition);
// create new working node
// a fresh node is needed to only display the actual drawing process for
// the undo function
if (this->GetToolManager()->GetDataStorage()->Exists(m_WorkingNode))
this->GetToolManager()->GetDataStorage()->Remove(m_WorkingNode);
m_WorkingSlice = nullptr;
m_CurrentPlane = nullptr;
m_WorkingNode = DataNode::New();
m_WorkingNode->SetProperty("levelwindow", mitk::LevelWindowProperty::New(mitk::LevelWindow(0, 1)));
m_WorkingNode->SetProperty("binary", mitk::BoolProperty::New(true));
this->m_WorkingNode->SetVisibility(true);
m_LastEventSender = positionEvent->GetSender();
m_LastEventSlice = m_LastEventSender->GetSlice();
m_MasterContour->SetClosed(true);
this->MouseMoved(interactionEvent, true);
}
void mitk::PaintbrushTool::OnMouseMoved(StateMachineAction *, InteractionEvent *interactionEvent)
{
MouseMoved(interactionEvent, false);
}
void mitk::PaintbrushTool::OnPrimaryButtonPressedMoved(StateMachineAction *, InteractionEvent *interactionEvent)
{
MouseMoved(interactionEvent, true);
}
/**
Insert the point to the feedback contour,finish to build the contour and at the same time the painting function
*/
void mitk::PaintbrushTool::MouseMoved(mitk::InteractionEvent *interactionEvent, bool leftMouseButtonPressed)
{
auto *positionEvent = dynamic_cast<mitk::InteractionPositionEvent *>(interactionEvent);
CheckIfCurrentSliceHasChanged(positionEvent);
if (m_LastContourSize != m_Size)
{
UpdateContour(positionEvent);
m_LastContourSize = m_Size;
}
Point3D worldCoordinates = positionEvent->GetPositionInWorld();
Point3D indexCoordinates;
m_WorkingSlice->GetGeometry()->WorldToIndex(worldCoordinates, indexCoordinates);
// round to nearest voxel center (abort if this hasn't changed)
if (m_Size % 2 == 0) // even
{
indexCoordinates[0] = std::round(indexCoordinates[0]);
indexCoordinates[1] = std::round(indexCoordinates[1]);
}
else // odd
{
indexCoordinates[0] = std::round(indexCoordinates[0]);
indexCoordinates[1] = std::round(indexCoordinates[1]);
}
static Point3D lastPos; // uninitialized: if somebody finds out how this can be initialized in a one-liner, tell me
if (fabs(indexCoordinates[0] - lastPos[0]) > mitk::eps || fabs(indexCoordinates[1] - lastPos[1]) > mitk::eps ||
fabs(indexCoordinates[2] - lastPos[2]) > mitk::eps || leftMouseButtonPressed)
{
lastPos = indexCoordinates;
}
else
{
return;
}
auto contour = ContourModel::New();
contour->SetClosed(true);
auto it = m_MasterContour->Begin();
auto end = m_MasterContour->End();
while (it != end)
{
auto point = (*it)->Coordinates;
point[0] += indexCoordinates[0];
point[1] += indexCoordinates[1];
contour->AddVertex(point);
++it;
}
if (leftMouseButtonPressed)
{
const double dist = indexCoordinates.EuclideanDistanceTo(m_LastPosition);
const double radius = static_cast<double>(m_Size) / 2.0;
DataNode *workingNode(this->GetToolManager()->GetWorkingData(0));
auto workingImage = dynamic_cast<Image*>(workingNode->GetData());
int activePixelValue = ContourModelUtils::GetActivePixelValue(workingImage);
// m_PaintingPixelValue only decides whether to paint or erase
mitk::ContourModelUtils::FillContourInSlice(
contour, m_WorkingSlice, workingImage, m_PaintingPixelValue * activePixelValue);
m_WorkingNode->SetData(m_WorkingSlice);
m_WorkingNode->Modified();
// if points are >= radius away draw rectangle to fill empty holes
// in between the 2 points
if (dist > radius)
{
const mitk::Point3D &currentPos = indexCoordinates;
mitk::Point3D direction;
mitk::Point3D vertex;
mitk::Point3D normal;
direction[0] = indexCoordinates[0] - m_LastPosition[0];
direction[1] = indexCoordinates[1] - m_LastPosition[1];
direction[2] = indexCoordinates[2] - m_LastPosition[2];
direction[0] = direction.GetVnlVector().normalize()[0];
direction[1] = direction.GetVnlVector().normalize()[1];
direction[2] = direction.GetVnlVector().normalize()[2];
// 90 degrees rotation of direction
normal[0] = -1.0 * direction[1];
normal[1] = direction[0];
contour->Clear();
// upper left corner
vertex[0] = m_LastPosition[0] + (normal[0] * radius);
vertex[1] = m_LastPosition[1] + (normal[1] * radius);
contour->AddVertex(vertex);
// upper right corner
vertex[0] = currentPos[0] + (normal[0] * radius);
vertex[1] = currentPos[1] + (normal[1] * radius);
contour->AddVertex(vertex);
// lower right corner
vertex[0] = currentPos[0] - (normal[0] * radius);
vertex[1] = currentPos[1] - (normal[1] * radius);
contour->AddVertex(vertex);
// lower left corner
vertex[0] = m_LastPosition[0] - (normal[0] * radius);
vertex[1] = m_LastPosition[1] - (normal[1] * radius);
contour->AddVertex(vertex);
mitk::ContourModelUtils::FillContourInSlice(contour, m_WorkingSlice, workingImage, m_PaintingPixelValue * activePixelValue);
m_WorkingNode->SetData(m_WorkingSlice);
m_WorkingNode->Modified();
}
}
else
{
// switched from different renderwindow
// no activate hover highlighting. Otherwise undo / redo wont work
this->m_WorkingNode->SetVisibility(false);
}
m_LastPosition = indexCoordinates;
// visualize contour
ContourModel::Pointer tmp =
FeedbackContourTool::BackProjectContourFrom2DSlice(m_WorkingSlice->GetGeometry(), contour);
this->UpdateCurrentFeedbackContour(tmp);
assert(positionEvent->GetSender()->GetRenderWindow());
RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow());
}
void mitk::PaintbrushTool::OnMouseReleased(StateMachineAction *, InteractionEvent *interactionEvent)
{
// When mouse is released write segmentationresult back into image
auto *positionEvent = dynamic_cast<mitk::InteractionPositionEvent *>(interactionEvent);
if (!positionEvent)
return;
this->WriteBackSegmentationResult(positionEvent, m_WorkingSlice->Clone());
// deactivate visibility of helper node
m_WorkingNode->SetVisibility(false);
RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow());
}
/**
Called when the CTRL key is pressed. Will change the painting pixel value from 0 to 1 or from 1 to 0.
*/
void mitk::PaintbrushTool::OnInvertLogic(StateMachineAction *, InteractionEvent *)
{
// Inversion only for 0 and 1 as painting values
if (m_PaintingPixelValue == 1)
{
m_PaintingPixelValue = 0;
FeedbackContourTool::SetFeedbackContourColor(1.0, 0.0, 0.0);
}
else if (m_PaintingPixelValue == 0)
{
m_PaintingPixelValue = 1;
FeedbackContourTool::SetFeedbackContourColorDefault();
}
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::PaintbrushTool::CheckIfCurrentSliceHasChanged(const InteractionPositionEvent *event)
{
- const PlaneGeometry *planeGeometry((event->GetSender()->GetCurrentWorldPlaneGeometry()));
- const auto *abstractTransformGeometry(
+ const PlaneGeometry* planeGeometry((event->GetSender()->GetCurrentWorldPlaneGeometry()));
+ const auto* abstractTransformGeometry(
dynamic_cast<const AbstractTransformGeometry *>(event->GetSender()->GetCurrentWorldPlaneGeometry()));
- DataNode *workingNode(this->GetToolManager()->GetWorkingData(0));
+ if (nullptr == planeGeometry || nullptr != abstractTransformGeometry)
+ {
+ return;
+ }
- if (!workingNode)
+ DataNode* workingNode = this->GetToolManager()->GetWorkingData(0);
+ if (nullptr == workingNode)
+ {
return;
+ }
Image::Pointer image = dynamic_cast<Image *>(workingNode->GetData());
-
- if (!image || !planeGeometry || abstractTransformGeometry)
+ if (nullptr == image)
+ {
return;
+ }
if (m_CurrentPlane.IsNull() || m_WorkingSlice.IsNull())
{
m_CurrentPlane = planeGeometry;
m_WorkingSlice = SegTool2D::GetAffectedImageSliceAs2DImage(event, image)->Clone();
- m_WorkingNode->ReplaceProperty("color", workingNode->GetProperty("color"));
m_WorkingNode->SetData(m_WorkingSlice);
}
else
{
bool isSameSlice(false);
isSameSlice = mitk::MatrixEqualElementWise(planeGeometry->GetIndexToWorldTransform()->GetMatrix(),
m_CurrentPlane->GetIndexToWorldTransform()->GetMatrix());
isSameSlice = mitk::Equal(planeGeometry->GetIndexToWorldTransform()->GetOffset(),
m_CurrentPlane->GetIndexToWorldTransform()->GetOffset());
if (!isSameSlice)
{
this->GetToolManager()->GetDataStorage()->Remove(m_WorkingNode);
- m_CurrentPlane = nullptr;
- m_WorkingSlice = nullptr;
- m_WorkingNode = nullptr;
m_CurrentPlane = planeGeometry;
m_WorkingSlice = SegTool2D::GetAffectedImageSliceAs2DImage(event, image)->Clone();
m_WorkingNode = mitk::DataNode::New();
m_WorkingNode->SetProperty("levelwindow", mitk::LevelWindowProperty::New(mitk::LevelWindow(0, 1)));
m_WorkingNode->SetProperty("binary", mitk::BoolProperty::New(true));
m_WorkingNode->SetData(m_WorkingSlice);
// So that the paintbrush contour vanished in the previous render window
RenderingManager::GetInstance()->RequestUpdateAll();
}
}
+ mitk::Color currentColor;
+ if (m_PaintingPixelValue == 1)
+ {
+ currentColor.Set(0.0, 1.0, 0.);
+ }
+ else
+ {
+ currentColor.Set(1.0, 0.0, 0.);
+ }
+ m_WorkingNode->SetProperty("color", mitk::ColorProperty::New(currentColor[0], currentColor[1], currentColor[2]));
+
if (!this->GetToolManager()->GetDataStorage()->Exists(m_WorkingNode))
{
m_WorkingNode->SetProperty("outline binary", mitk::BoolProperty::New(true));
- m_WorkingNode->SetProperty("color", workingNode->GetProperty("color"));
m_WorkingNode->SetProperty("name", mitk::StringProperty::New("Paintbrush_Node"));
m_WorkingNode->SetProperty("helper object", mitk::BoolProperty::New(true));
m_WorkingNode->SetProperty("opacity", mitk::FloatProperty::New(0.8));
m_WorkingNode->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false));
m_WorkingNode->SetVisibility(
false, mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3")));
this->GetToolManager()->GetDataStorage()->Add(m_WorkingNode);
}
}
void mitk::PaintbrushTool::OnToolManagerWorkingDataModified()
{
// Here we simply set the current working slice to null. The next time the mouse is moved
// within a renderwindow a new slice will be extracted from the new working data
m_WorkingSlice = nullptr;
}
diff --git a/Modules/Segmentation/Interactions/mitkTool.cpp b/Modules/Segmentation/Interactions/mitkTool.cpp
index fb9721e97c..b5d0b702ed 100644
--- a/Modules/Segmentation/Interactions/mitkTool.cpp
+++ b/Modules/Segmentation/Interactions/mitkTool.cpp
@@ -1,348 +1,353 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkTool.h"
#include <mitkAnatomicalStructureColorPresets.h>
#include "mitkDisplayInteractor.h"
#include "mitkDisplayActionEventBroadcast.h"
#include "mitkImageReadAccessor.h"
#include "mitkImageWriteAccessor.h"
#include "mitkLevelWindowProperty.h"
#include "mitkLookupTableProperty.h"
#include "mitkProperties.h"
#include "mitkVtkResliceInterpolationProperty.h"
#include <mitkDICOMSegmentationPropertyHelper.cpp>
#include <mitkToolManager.h>
// us
#include <usGetModuleContext.h>
#include <usModuleResource.h>
// itk
#include <itkObjectFactory.h>
+namespace mitk
+{
+ itkEventMacroDefinition(ToolEvent, itk::ModifiedEvent);
+}
+
mitk::Tool::Tool(const char *type, const us::Module *interactorModule)
: m_EventConfig(""),
m_ToolManager(nullptr),
m_PredicateImages(NodePredicateDataType::New("Image")), // for reference images
m_PredicateDim3(NodePredicateDimension::New(3, 1)),
m_PredicateDim4(NodePredicateDimension::New(4, 1)),
m_PredicateDimension(mitk::NodePredicateOr::New(m_PredicateDim3, m_PredicateDim4)),
m_PredicateImage3D(NodePredicateAnd::New(m_PredicateImages, m_PredicateDimension)),
m_PredicateBinary(NodePredicateProperty::New("binary", BoolProperty::New(true))),
m_PredicateNotBinary(NodePredicateNot::New(m_PredicateBinary)),
m_PredicateSegmentation(NodePredicateProperty::New("segmentation", BoolProperty::New(true))),
m_PredicateNotSegmentation(NodePredicateNot::New(m_PredicateSegmentation)),
m_PredicateHelper(NodePredicateProperty::New("helper object", BoolProperty::New(true))),
m_PredicateNotHelper(NodePredicateNot::New(m_PredicateHelper)),
m_PredicateImageColorful(NodePredicateAnd::New(m_PredicateNotBinary, m_PredicateNotSegmentation)),
m_PredicateImageColorfulNotHelper(NodePredicateAnd::New(m_PredicateImageColorful, m_PredicateNotHelper)),
m_PredicateReference(NodePredicateAnd::New(m_PredicateImage3D, m_PredicateImageColorfulNotHelper)),
m_IsSegmentationPredicate(
NodePredicateAnd::New(NodePredicateOr::New(m_PredicateBinary, m_PredicateSegmentation), m_PredicateNotHelper)),
m_InteractorType(type),
m_DisplayInteractorConfigs(),
m_InteractorModule(interactorModule)
{
}
mitk::Tool::~Tool()
{
}
bool mitk::Tool::CanHandle(const BaseData* referenceData, const BaseData* /*workingData*/) const
{
if (referenceData == nullptr)
return false;
return true;
}
void mitk::Tool::InitializeStateMachine()
{
if (m_InteractorType.empty())
return;
try
{
auto isThisModule = nullptr == m_InteractorModule;
auto module = isThisModule
? us::GetModuleContext()->GetModule()
: m_InteractorModule;
LoadStateMachine(m_InteractorType + ".xml", module);
SetEventConfig(isThisModule ? "SegmentationToolsConfig.xml" : m_InteractorType + "Config.xml", module);
}
catch (const std::exception &e)
{
MITK_ERROR << "Could not load statemachine pattern " << m_InteractorType << ".xml with exception: " << e.what();
}
}
void mitk::Tool::Notify(InteractionEvent *interactionEvent, bool isHandled)
{
// to use the state machine pattern,
// the event is passed to the state machine interface to be handled
if (!isHandled)
{
this->HandleEvent(interactionEvent, nullptr);
}
}
void mitk::Tool::ConnectActionsAndFunctions()
{
}
bool mitk::Tool::FilterEvents(InteractionEvent *, DataNode *)
{
return true;
}
const char *mitk::Tool::GetGroup() const
{
return "default";
}
void mitk::Tool::SetToolManager(ToolManager *manager)
{
m_ToolManager = manager;
}
mitk::ToolManager* mitk::Tool::GetToolManager() const
{
return m_ToolManager;
}
mitk::DataStorage* mitk::Tool::GetDataStorage() const
{
if (nullptr != m_ToolManager)
{
return m_ToolManager->GetDataStorage();
}
return nullptr;
}
void mitk::Tool::Activated()
{
// As a legacy solution the display interaction of the new interaction framework is disabled here to avoid conflicts
// with tools
// Note: this only affects InteractionEventObservers (formerly known as Listeners) all DataNode specific interaction
// will still be enabled
m_DisplayInteractorConfigs.clear();
std::vector<us::ServiceReference<InteractionEventObserver>> listEventObserver =
us::GetModuleContext()->GetServiceReferences<InteractionEventObserver>();
for (auto it = listEventObserver.begin(); it != listEventObserver.end(); ++it)
{
auto displayInteractor = dynamic_cast<DisplayInteractor*>(us::GetModuleContext()->GetService<InteractionEventObserver>(*it));
if (displayInteractor != nullptr)
{
// remember the original configuration
m_DisplayInteractorConfigs.insert(std::make_pair(*it, displayInteractor->GetEventConfig()));
// here the alternative configuration is loaded
displayInteractor->AddEventConfig(m_EventConfig.c_str());
}
auto displayActionEventBroadcast = dynamic_cast<DisplayActionEventBroadcast*>(us::GetModuleContext()->GetService<InteractionEventObserver>(*it));
if (displayActionEventBroadcast != nullptr)
{
// remember the original configuration
m_DisplayInteractorConfigs.insert(std::make_pair(*it, displayActionEventBroadcast->GetEventConfig()));
// here the alternative configuration is loaded
displayActionEventBroadcast->AddEventConfig(m_EventConfig.c_str());
}
}
}
void mitk::Tool::Deactivated()
{
// Re-enabling InteractionEventObservers that have been previously disabled for legacy handling of Tools
// in new interaction framework
for (auto it = m_DisplayInteractorConfigs.begin(); it != m_DisplayInteractorConfigs.end(); ++it)
{
if (it->first)
{
auto displayInteractor = static_cast<DisplayInteractor*>(us::GetModuleContext()->GetService<InteractionEventObserver>(it->first));
if (displayInteractor != nullptr)
{
// here the regular configuration is loaded again
displayInteractor->SetEventConfig(it->second);
}
auto displayActionEventBroadcast = dynamic_cast<DisplayActionEventBroadcast*>(us::GetModuleContext()->GetService<InteractionEventObserver>(it->first));
if (displayActionEventBroadcast != nullptr)
{
// here the regular configuration is loaded again
displayActionEventBroadcast->SetEventConfig(it->second);
}
}
}
m_DisplayInteractorConfigs.clear();
}
itk::Object::Pointer mitk::Tool::GetGUI(const std::string &toolkitPrefix, const std::string &toolkitPostfix)
{
itk::Object::Pointer object;
std::string classname = this->GetNameOfClass();
std::string guiClassname = toolkitPrefix + classname + toolkitPostfix;
std::list<itk::LightObject::Pointer> allGUIs = itk::ObjectFactoryBase::CreateAllInstance(guiClassname.c_str());
for (auto iter = allGUIs.begin(); iter != allGUIs.end(); ++iter)
{
if (object.IsNull())
{
object = dynamic_cast<itk::Object *>(iter->GetPointer());
}
else
{
MITK_ERROR << "There is more than one GUI for " << classname << " (several factories claim ability to produce a "
<< guiClassname << " ) " << std::endl;
return nullptr; // people should see and fix this error
}
}
return object;
}
mitk::NodePredicateBase::ConstPointer mitk::Tool::GetReferenceDataPreference() const
{
return m_PredicateReference.GetPointer();
}
mitk::NodePredicateBase::ConstPointer mitk::Tool::GetWorkingDataPreference() const
{
return m_IsSegmentationPredicate.GetPointer();
}
mitk::DataNode::Pointer mitk::Tool::CreateEmptySegmentationNode(const Image *original,
const std::string &organName,
const mitk::Color &color) const
{
// we NEED a reference image for size etc.
if (!original)
return nullptr;
// actually create a new empty segmentation
PixelType pixelType(mitk::MakeScalarPixelType<DefaultSegmentationDataType>());
LabelSetImage::Pointer segmentation = LabelSetImage::New();
if (original->GetDimension() == 2)
{
const unsigned int dimensions[] = {original->GetDimension(0), original->GetDimension(1), 1};
segmentation->Initialize(pixelType, 3, dimensions);
segmentation->AddLayer();
}
else
{
segmentation->Initialize(original);
}
mitk::Label::Pointer label = mitk::Label::New();
label->SetName(organName);
label->SetColor(color);
label->SetValue(1);
segmentation->GetActiveLabelSet()->AddLabel(label);
segmentation->GetActiveLabelSet()->SetActiveLabel(1);
unsigned int byteSize = sizeof(mitk::Label::PixelType);
if (segmentation->GetDimension() < 4)
{
for (unsigned int dim = 0; dim < segmentation->GetDimension(); ++dim)
{
byteSize *= segmentation->GetDimension(dim);
}
mitk::ImageWriteAccessor writeAccess(segmentation.GetPointer(), segmentation->GetVolumeData(0));
memset(writeAccess.GetData(), 0, byteSize);
}
else
{
// if we have a time-resolved image we need to set memory to 0 for each time step
for (unsigned int dim = 0; dim < 3; ++dim)
{
byteSize *= segmentation->GetDimension(dim);
}
for (unsigned int volumeNumber = 0; volumeNumber < segmentation->GetDimension(3); volumeNumber++)
{
mitk::ImageWriteAccessor writeAccess(segmentation.GetPointer(), segmentation->GetVolumeData(volumeNumber));
memset(writeAccess.GetData(), 0, byteSize);
}
}
if (original->GetTimeGeometry())
{
TimeGeometry::Pointer originalGeometry = original->GetTimeGeometry()->Clone();
segmentation->SetTimeGeometry(originalGeometry);
}
else
{
Tool::ErrorMessage("Original image does not have a 'Time sliced geometry'! Cannot create a segmentation.");
return nullptr;
}
return CreateSegmentationNode(segmentation, organName, color);
}
mitk::DataNode::Pointer mitk::Tool::CreateSegmentationNode(Image *image,
const std::string &organName,
const mitk::Color &color) const
{
if (!image)
return nullptr;
// decorate the datatreenode with some properties
DataNode::Pointer segmentationNode = DataNode::New();
segmentationNode->SetData(image);
// name
segmentationNode->SetProperty("name", StringProperty::New(organName));
// visualization properties
segmentationNode->SetProperty("binary", BoolProperty::New(true));
segmentationNode->SetProperty("color", ColorProperty::New(color));
mitk::LookupTable::Pointer lut = mitk::LookupTable::New();
lut->SetType(mitk::LookupTable::MULTILABEL);
mitk::LookupTableProperty::Pointer lutProp = mitk::LookupTableProperty::New();
lutProp->SetLookupTable(lut);
segmentationNode->SetProperty("LookupTable", lutProp);
segmentationNode->SetProperty("texture interpolation", BoolProperty::New(false));
segmentationNode->SetProperty("layer", IntProperty::New(10));
segmentationNode->SetProperty("levelwindow", LevelWindowProperty::New(LevelWindow(0.5, 1)));
segmentationNode->SetProperty("opacity", FloatProperty::New(0.3));
segmentationNode->SetProperty("segmentation", BoolProperty::New(true));
segmentationNode->SetProperty("reslice interpolation",
VtkResliceInterpolationProperty::New()); // otherwise -> segmentation appears in 2
// slices sometimes (only visual effect, not
// different data)
// For MITK-3M3 release, the volume of all segmentations should be shown
segmentationNode->SetProperty("showVolume", BoolProperty::New(true));
return segmentationNode;
}
us::ModuleResource mitk::Tool::GetIconResource() const
{
// Each specific tool should load its own resource. This one will be invalid
return us::ModuleResource();
}
us::ModuleResource mitk::Tool::GetCursorIconResource() const
{
// Each specific tool should load its own resource. This one will be invalid
return us::ModuleResource();
}
diff --git a/Modules/Segmentation/Interactions/mitkToolEvents.h b/Modules/Segmentation/Interactions/mitkToolEvents.h
index 9f615ea63b..463b8acfa0 100644
--- a/Modules/Segmentation/Interactions/mitkToolEvents.h
+++ b/Modules/Segmentation/Interactions/mitkToolEvents.h
@@ -1,212 +1,212 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITK_TOOL_EVENTS_H
#define MITK_TOOL_EVENTS_H
#pragma GCC visibility push(default)
#include <itkEventObject.h>
#pragma GCC visibility pop
namespace mitk
{
/**
\brief Basic tool event without any parameters
Can simply be inherited using the itkEventMacro, e.g.
\code
namespace mitk
{
class MyTool : public Tool
{
public:
itkEventMacro(MySpecialEvent, ToolEvent);
[...]
protected:
// Invoke your event like this
void YourExampleMethod()
{
InvokeEvent( MySpecialEvent() );
}
};
}
\endcode
*/
#pragma GCC visibility push(default)
- itkEventMacro(ToolEvent, itk::ModifiedEvent);
+ itkEventMacroDeclaration(ToolEvent, itk::ModifiedEvent); // Definition is in mitkTool.cpp
#pragma GCC visibility pop
/**
\brief Tool event with 1 parameter
Can store one parameter for use within an observer. To derive your own special events, use the
mitkToolEventMacro1Param macro.
\code
namespace mitk
{
class MyTool : public Tool
{
public:
mitkToolEventMacro1Param(FooToolEvent, int);
[...]
protected:
// Invoke your event like this
void YourExampleMethod()
{
InvokeEvent( FooToolEvent(32) );
}
};
}
\endcode
*/
template <typename T>
class ParameterToolEvent : public ToolEvent
{
public:
typedef ParameterToolEvent Self;
typedef ToolEvent Superclass;
ParameterToolEvent(const T parameter) : m_Parameter(parameter) {}
ParameterToolEvent(const Self &s) : ToolEvent(s), m_Parameter(s.m_Parameter) {}
~ParameterToolEvent() override {}
const char *GetEventName() const override { return "ParameterToolEvent"; }
bool CheckEvent(const ::itk::EventObject *e) const override { return dynamic_cast<const Self *>(e); }
::itk::EventObject *MakeObject() const override { return new Self(m_Parameter); }
const T GetParameter() const { return m_Parameter; }
protected:
const T m_Parameter;
private:
ParameterToolEvent();
void operator=(const Self &);
};
/**
\brief Tool event with 1 parameter
Can store one parameter for use within an observer. To derive your own special events, use the
mitkToolEventMacro1Param macro.
\code
namespace mitk
{
class MyTool : public Tool
{
public:
mitkToolEventMacro1Param(FooToolEvent, int);
[...]
protected:
// Invoke your event like this
void YourExampleMethod()
{
InvokeEvent( BarToolEvent(32, false) );
}
};
}
\endcode
*/
template <typename T, typename U>
class TwoParameterToolEvent : public ToolEvent
{
public:
typedef TwoParameterToolEvent Self;
typedef ToolEvent Superclass;
TwoParameterToolEvent(const T parameter1, const U parameter2) : m_Parameter1(parameter1), m_Parameter2(parameter2)
{
}
TwoParameterToolEvent(const Self &s) : ToolEvent(s), m_Parameter1(s.m_Parameter1), m_Parameter2(s.m_Parameter2) {}
~TwoParameterToolEvent() override {}
const char *GetEventName() const override { return "TwoParameterToolEvent"; }
bool CheckEvent(const ::itk::EventObject *e) const override { return dynamic_cast<const Self *>(e); }
::itk::EventObject *MakeObject() const override { return new Self(m_Parameter1, m_Parameter2); }
const T GetParameter1() const { return m_Parameter1; }
const T GetParameter2() const { return m_Parameter2; }
protected:
const T m_Parameter1;
const U m_Parameter2;
private:
TwoParameterToolEvent();
void operator=(const Self &);
};
typedef ParameterToolEvent<int> IntegerToolEvent;
typedef ParameterToolEvent<float> FloatToolEvent;
typedef ParameterToolEvent<bool> BoolToolEvent;
} // namespace
// some macros to let tools define their own event classes as inner classes (should then inherit from something like
// FloatToolEvent
// inheritance, because it allows observers to distinguish events
#define mitkToolEventMacro(eventname, baseevent) \
\
class eventname : public baseevent\
{virtual const char * GetEventName() const {return #eventname; \
} \
\
} \
;
#define mitkToolEventMacro1Param(eventname, paramtype1) \
\
class eventname : public ParameterToolEvent<paramtype1> \
\
{ \
public: \
virtual const char *GetEventName() const { return #eventname "(" #paramtype1 ")"; } \
eventname(const paramtype1 parameter) : ParameterToolEvent<paramtype1>(parameter) {} \
private: \
eventname(); \
\
};
#define mitkToolEventMacro2Param(eventname, paramtype1, paramtype2) \
\
class eventname : public TwoParameterToolEvent<paramtype1, paramtype2> \
\
{ \
public: \
virtual const char *GetEventName() const { return #eventname "(" #paramtype1 "," #paramtype2 ")"; } \
eventname(const paramtype1 parameter1, const paramtype2 parameter2) \
: TwoParameterToolEvent<paramtype1, paramtype2>(parameter1, parameter2) \
{ \
} \
\
private: \
eventname(); \
\
};
#endif
diff --git a/Modules/Segmentation/Interactions/mitkWatershedTool.cpp b/Modules/Segmentation/Interactions/mitkWatershedTool.cpp
index b533bbe242..358cade477 100644
--- a/Modules/Segmentation/Interactions/mitkWatershedTool.cpp
+++ b/Modules/Segmentation/Interactions/mitkWatershedTool.cpp
@@ -1,163 +1,163 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkWatershedTool.h"
#include "mitkIOUtil.h"
#include "mitkITKImageImport.h"
#include "mitkImage.h"
#include "mitkLabelSetImage.h"
#include "mitkImageAccessByItk.h"
#include "mitkImageCast.h"
#include "mitkImageStatisticsHolder.h"
#include "mitkLevelWindowManager.h"
#include "mitkLookupTable.h"
#include "mitkLookupTableProperty.h"
#include "mitkProgressBar.h"
#include "mitkRenderingManager.h"
#include "mitkRenderingModeProperty.h"
#include "mitkToolCommand.h"
#include "mitkToolManager.h"
#include <mitkSliceNavigationController.h>
#include <usGetModuleContext.h>
#include <usModule.h>
#include <usModuleContext.h>
#include <usModuleResource.h>
#include <vtkLookupTable.h>
-#include <itkExceptionObject.h>
+#include <itkMacro.h>
#include <itkGradientMagnitudeRecursiveGaussianImageFilter.h>
#include <itkWatershedImageFilter.h>
namespace mitk
{
MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, WatershedTool, "Watershed tool");
}
void mitk::WatershedTool::Activated()
{
Superclass::Activated();
m_Level = 0.0;
m_Threshold = 0.0;
m_MagFilter = nullptr;
m_WatershedFilter = nullptr;
m_LastFilterInput = nullptr;
}
us::ModuleResource mitk::WatershedTool::GetIconResource() const
{
us::Module *module = us::GetModuleContext()->GetModule();
us::ModuleResource resource = module->GetResource("Watershed_48x48.png");
return resource;
}
const char **mitk::WatershedTool::GetXPM() const
{
return nullptr;
}
const char *mitk::WatershedTool::GetName() const
{
return "Watershed";
}
mitk::LabelSetImage::Pointer mitk::WatershedTool::ComputeMLPreview(const Image* inputAtTimeStep, TimeStepType /*timeStep*/)
{
mitk::LabelSetImage::Pointer labelSetOutput;
try
{
mitk::Image::Pointer output;
bool inputChanged = inputAtTimeStep != m_LastFilterInput;
// create and run itk filter pipeline
AccessByItk_2(inputAtTimeStep, ITKWatershed, output, inputChanged);
labelSetOutput = mitk::LabelSetImage::New();
labelSetOutput->InitializeByLabeledImage(output);
}
catch (itk::ExceptionObject & e)
{
//force reset of filters as they might be in an invalid state now.
m_MagFilter = nullptr;
m_WatershedFilter = nullptr;
m_LastFilterInput = nullptr;
MITK_ERROR << "Watershed Filter Error: " << e.GetDescription();
}
m_LastFilterInput = inputAtTimeStep;
return labelSetOutput;
}
template <typename TPixel, unsigned int VImageDimension>
void mitk::WatershedTool::ITKWatershed(const itk::Image<TPixel, VImageDimension>* originalImage,
mitk::Image::Pointer& segmentation, bool inputChanged)
{
typedef itk::WatershedImageFilter<itk::Image<float, VImageDimension>> WatershedFilter;
typedef itk::GradientMagnitudeRecursiveGaussianImageFilter<itk::Image<TPixel, VImageDimension>,
itk::Image<float, VImageDimension>>
MagnitudeFilter;
// We create the filter pipeline only once (if needed) and not everytime we
// generate the ml image preview.
// Reason: If only the levels are changed the update of the pipe line is very
// fast and we want to profit from this feature.
// at first add a gradient magnitude filter
typename MagnitudeFilter::Pointer magnitude = dynamic_cast<MagnitudeFilter*>(m_MagFilter.GetPointer());
if (magnitude.IsNull())
{
magnitude = MagnitudeFilter::New();
magnitude->SetSigma(1.0);
magnitude->AddObserver(itk::ProgressEvent(), m_ProgressCommand);
m_MagFilter = magnitude.GetPointer();
}
if (inputChanged)
{
magnitude->SetInput(originalImage);
}
// then add the watershed filter to the pipeline
typename WatershedFilter::Pointer watershed = dynamic_cast<WatershedFilter*>(m_WatershedFilter.GetPointer());
if (watershed.IsNull())
{
watershed = WatershedFilter::New();
watershed->SetInput(magnitude->GetOutput());
watershed->AddObserver(itk::ProgressEvent(), m_ProgressCommand);
m_WatershedFilter = watershed.GetPointer();
}
watershed->SetThreshold(m_Threshold);
watershed->SetLevel(m_Level);
watershed->Update();
// then make sure, that the output has the desired pixel type
typedef itk::CastImageFilter<typename WatershedFilter::OutputImageType,
itk::Image<Tool::DefaultSegmentationDataType, VImageDimension>>
CastFilter;
typename CastFilter::Pointer cast = CastFilter::New();
cast->SetInput(watershed->GetOutput());
// start the whole pipeline
cast->Update();
// since we obtain a new image from our pipeline, we have to make sure, that our mitk::Image::Pointer
// is responsible for the memory management of the output image
segmentation = mitk::GrabItkImageMemory(cast->GetOutput());
}
diff --git a/Modules/Segmentation/Interactions/mitknnUnetTool.cpp b/Modules/Segmentation/Interactions/mitknnUnetTool.cpp
index c2db0bcca3..97d9f9df2f 100644
--- a/Modules/Segmentation/Interactions/mitknnUnetTool.cpp
+++ b/Modules/Segmentation/Interactions/mitknnUnetTool.cpp
@@ -1,322 +1,322 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitknnUnetTool.h"
#include "mitkIOUtil.h"
#include "mitkProcessExecutor.h"
#include <itksys/SystemTools.hxx>
#include <usGetModuleContext.h>
#include <usModule.h>
#include <usModuleContext.h>
#include <usModuleResource.h>
namespace mitk
{
MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, nnUNetTool, "nnUNet tool");
}
mitk::nnUNetTool::nnUNetTool()
{
this->SetMitkTempDir(IOUtil::CreateTemporaryDirectory("mitk-XXXXXX"));
}
mitk::nnUNetTool::~nnUNetTool()
{
itksys::SystemTools::RemoveADirectory(this->GetMitkTempDir());
}
void mitk::nnUNetTool::Activated()
{
Superclass::Activated();
m_InputOutputPair = std::make_pair(nullptr, nullptr);
}
void mitk::nnUNetTool::UpdateCleanUp()
{
// This overriden method is intentionally left out for setting later upon demand
// in the `RenderOutputBuffer` method.
}
void mitk::nnUNetTool::RenderOutputBuffer()
{
if (this->m_OutputBuffer != nullptr)
{
Superclass::SetNodeProperties(this->m_OutputBuffer);
this->ClearOutputBuffer();
try
{
if (nullptr != this->GetPreviewSegmentationNode())
{
this->GetPreviewSegmentationNode()->SetVisibility(!this->GetSelectedLabels().empty());
}
if (this->GetSelectedLabels().empty())
{
this->ResetPreviewNode();
}
}
catch (const mitk::Exception &e)
{
MITK_INFO << e.GetDescription();
}
}
}
void mitk::nnUNetTool::SetNodeProperties(LabelSetImage::Pointer segmentation)
{
// This overriden method doesn't set node properties. Intentionally left out for setting later upon demand
// in the `RenderOutputBuffer` method.
this->m_OutputBuffer = segmentation;
}
mitk::LabelSetImage::Pointer mitk::nnUNetTool::GetOutputBuffer()
{
return this->m_OutputBuffer;
}
void mitk::nnUNetTool::ClearOutputBuffer()
{
this->m_OutputBuffer = nullptr;
}
us::ModuleResource mitk::nnUNetTool::GetIconResource() const
{
us::Module *module = us::GetModuleContext()->GetModule();
- us::ModuleResource resource = module->GetResource("Watershed_48x48.png");
+ us::ModuleResource resource = module->GetResource("AI_48x48.png");
return resource;
}
const char **mitk::nnUNetTool::GetXPM() const
{
return nullptr;
}
const char *mitk::nnUNetTool::GetName() const
{
return "nnUNet";
}
mitk::DataStorage *mitk::nnUNetTool::GetDataStorage()
{
return this->GetToolManager()->GetDataStorage();
}
mitk::DataNode *mitk::nnUNetTool::GetRefNode()
{
return this->GetToolManager()->GetReferenceData(0);
}
namespace
{
void onPythonProcessEvent(itk::Object * /*pCaller*/, const itk::EventObject &e, void *)
{
std::string testCOUT;
std::string testCERR;
const auto *pEvent = dynamic_cast<const mitk::ExternalProcessStdOutEvent *>(&e);
if (pEvent)
{
testCOUT = testCOUT + pEvent->GetOutput();
MITK_INFO << testCOUT;
}
const auto *pErrEvent = dynamic_cast<const mitk::ExternalProcessStdErrEvent *>(&e);
if (pErrEvent)
{
testCERR = testCERR + pErrEvent->GetOutput();
MITK_ERROR << testCERR;
}
}
} // namespace
mitk::LabelSetImage::Pointer mitk::nnUNetTool::ComputeMLPreview(const Image *inputAtTimeStep, TimeStepType /*timeStep*/)
{
if (m_InputOutputPair.first == inputAtTimeStep)
{
return m_InputOutputPair.second;
}
std::string inDir, outDir, inputImagePath, outputImagePath, scriptPath;
std::string templateFilename = "XXXXXX_000_0000.nii.gz";
ProcessExecutor::Pointer spExec = ProcessExecutor::New();
itk::CStyleCommand::Pointer spCommand = itk::CStyleCommand::New();
spCommand->SetCallback(&onPythonProcessEvent);
spExec->AddObserver(ExternalProcessOutputEvent(), spCommand);
ProcessExecutor::ArgumentListType args;
inDir = IOUtil::CreateTemporaryDirectory("nnunet-in-XXXXXX", this->GetMitkTempDir());
std::ofstream tmpStream;
inputImagePath = IOUtil::CreateTemporaryFile(tmpStream, templateFilename, inDir + IOUtil::GetDirectorySeparator());
tmpStream.close();
std::size_t found = inputImagePath.find_last_of(IOUtil::GetDirectorySeparator());
std::string fileName = inputImagePath.substr(found + 1);
std::string token = fileName.substr(0, fileName.find("_"));
if (this->GetNoPip())
{
scriptPath = this->GetnnUNetDirectory() + IOUtil::GetDirectorySeparator() + "nnunet" +
IOUtil::GetDirectorySeparator() + "inference" + IOUtil::GetDirectorySeparator() + "predict_simple.py";
}
try
{
IOUtil::Save(inputAtTimeStep, inputImagePath);
if (this->GetMultiModal())
{
for (size_t i = 0; i < this->m_OtherModalPaths.size(); ++i)
{
mitk::Image::ConstPointer modalImage = this->m_OtherModalPaths[i];
std::string outModalFile =
inDir + IOUtil::GetDirectorySeparator() + token + "_000_000" + std::to_string(i + 1) + ".nii.gz";
IOUtil::Save(modalImage.GetPointer(), outModalFile);
}
}
}
catch (const mitk::Exception &e)
{
/*
Can't throw mitk exception to the caller. Refer: T28691
*/
MITK_ERROR << e.GetDescription();
return nullptr;
}
// Code calls external process
std::string command = "nnUNet_predict";
if (this->GetNoPip())
{
#ifdef _WIN32
command = "python";
#else
command = "python3";
#endif
}
for (ModelParams &modelparam : m_ParamQ)
{
outDir = IOUtil::CreateTemporaryDirectory("nnunet-out-XXXXXX", this->GetMitkTempDir());
outputImagePath = outDir + IOUtil::GetDirectorySeparator() + token + "_000.nii.gz";
modelparam.outputDir = outDir;
args.clear();
if (this->GetNoPip())
{
args.push_back(scriptPath);
}
args.push_back("-i");
args.push_back(inDir);
args.push_back("-o");
args.push_back(outDir);
args.push_back("-t");
args.push_back(modelparam.task);
if (modelparam.model.find("cascade") != std::string::npos)
{
args.push_back("-ctr");
}
else
{
args.push_back("-tr");
}
args.push_back(modelparam.trainer);
args.push_back("-m");
args.push_back(modelparam.model);
args.push_back("-p");
args.push_back(modelparam.planId);
if (!modelparam.folds.empty())
{
args.push_back("-f");
for (auto fold : modelparam.folds)
{
args.push_back(fold);
}
}
args.push_back("--num_threads_nifti_save");
args.push_back("1"); // fixing to 1
if (!this->GetMirror())
{
args.push_back("--disable_tta");
}
if (!this->GetMixedPrecision())
{
args.push_back("--disable_mixed_precision");
}
if (this->GetEnsemble() && !this->GetPostProcessingJsonDirectory().empty())
{
args.push_back("--save_npz");
}
try
{
std::string resultsFolderEnv = "RESULTS_FOLDER=" + this->GetModelDirectory();
itksys::SystemTools::PutEnv(resultsFolderEnv.c_str());
std::string cudaEnv = "CUDA_VISIBLE_DEVICES=" + std::to_string(this->GetGpuId());
itksys::SystemTools::PutEnv(cudaEnv.c_str());
spExec->Execute(this->GetPythonPath(), command, args);
}
catch (const mitk::Exception &e)
{
/*
Can't throw mitk exception to the caller. Refer: T28691
*/
MITK_ERROR << e.GetDescription();
return nullptr;
}
}
if (this->GetEnsemble() && !this->GetPostProcessingJsonDirectory().empty())
{
args.clear();
command = "nnUNet_ensemble";
outDir = IOUtil::CreateTemporaryDirectory("nnunet-ensemble-out-XXXXXX", this->GetMitkTempDir());
outputImagePath = outDir + IOUtil::GetDirectorySeparator() + token + "_000.nii.gz";
args.push_back("-f");
for (ModelParams &modelparam : m_ParamQ)
{
args.push_back(modelparam.outputDir);
}
args.push_back("-o");
args.push_back(outDir);
args.push_back("-pp");
args.push_back(this->GetPostProcessingJsonDirectory());
spExec->Execute(this->GetPythonPath(), command, args);
}
try
{
LabelSetImage::Pointer resultImage = LabelSetImage::New();
Image::Pointer outputImage = IOUtil::Load<Image>(outputImagePath);
resultImage->InitializeByLabeledImage(outputImage);
resultImage->SetGeometry(inputAtTimeStep->GetGeometry());
m_InputOutputPair = std::make_pair(inputAtTimeStep, resultImage);
return resultImage;
}
catch (const mitk::Exception &e)
{
/*
Can't throw mitk exception to the caller. Refer: T28691
*/
MITK_ERROR << e.GetDescription();
return nullptr;
}
}
diff --git a/Modules/Segmentation/Rendering/mitkContourSetVtkMapper3D.cpp b/Modules/Segmentation/Rendering/mitkContourSetVtkMapper3D.cpp
index c1022373ea..1ee09b98ed 100644
--- a/Modules/Segmentation/Rendering/mitkContourSetVtkMapper3D.cpp
+++ b/Modules/Segmentation/Rendering/mitkContourSetVtkMapper3D.cpp
@@ -1,157 +1,155 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkContourSetVtkMapper3D.h"
#include "mitkColorProperty.h"
#include "mitkDataNode.h"
#include "mitkProperties.h"
#include "mitkVtkPropRenderer.h"
#include <vtkActor.h>
#include <vtkActor.h>
#include <vtkAppendPolyData.h>
#include <vtkAssembly.h>
#include <vtkCellArray.h>
#include <vtkFollower.h>
#include <vtkLinearTransform.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkPolygon.h>
#include <vtkProp3DCollection.h>
#include <vtkRenderer.h>
#include <vtkTubeFilter.h>
#include <cstdlib>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
mitk::ContourSetVtkMapper3D::ContourSetVtkMapper3D()
{
m_VtkPolyDataMapper = vtkPolyDataMapper::New();
m_Actor = vtkActor::New();
m_Actor->SetMapper(m_VtkPolyDataMapper);
m_ContourSet = vtkPolyData::New();
m_TubeFilter = vtkTubeFilter::New();
}
mitk::ContourSetVtkMapper3D::~ContourSetVtkMapper3D()
{
if (m_VtkPolyDataMapper)
m_VtkPolyDataMapper->Delete();
;
if (m_TubeFilter)
m_TubeFilter->Delete();
;
if (m_ContourSet)
m_ContourSet->Delete();
;
if (m_Actor)
m_Actor->Delete();
;
}
vtkProp *mitk::ContourSetVtkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/)
{
return m_Actor;
}
void mitk::ContourSetVtkMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer)
{
bool visible = true;
GetDataNode()->GetVisibility(visible, renderer, "visible");
if (!visible)
{
m_Actor->VisibilityOff();
return;
}
m_Actor->VisibilityOn();
if (renderer->GetCurrentWorldPlaneGeometryUpdateTime() > 0UL && renderer->GetCurrentWorldPlaneGeometryUpdateTime() < this->GetInput()->GetMTime())
{
m_ContourSet = vtkPolyData::New();
vtkPoints *points = vtkPoints::New();
vtkCellArray *lines = vtkCellArray::New();
mitk::ContourSet::Pointer input = const_cast<mitk::ContourSet *>(this->GetInput());
mitk::ContourSet::ContourVectorType contourVec = input->GetContours();
auto contourIt = contourVec.begin();
vtkIdType firstPointIndex = 0, lastPointIndex = 0;
vtkIdType ptIndex = 0;
while (contourIt != contourVec.end())
{
auto *nextContour = (mitk::Contour *)(*contourIt).second;
- Contour::InputType idx = nextContour->GetContourPath()->StartOfInput();
Contour::InputType end = nextContour->GetContourPath()->EndOfInput();
if (end > 50000)
end = 0;
mitk::Contour::PointsContainerPointer contourPoints = nextContour->GetPoints();
mitk::Contour::PointsContainerIterator pointsIt = contourPoints->Begin();
unsigned int counter = 0;
firstPointIndex = ptIndex;
while (pointsIt != contourPoints->End())
{
if (counter % 2 == 0)
{
Contour::BoundingBoxType::PointType point;
point = pointsIt.Value();
points->InsertPoint(ptIndex, point[0], point[1], point[2]);
if (ptIndex > firstPointIndex)
{
vtkIdType cell[2] = {ptIndex - 1, ptIndex};
lines->InsertNextCell((vtkIdType)2, cell);
}
lastPointIndex = ptIndex;
ptIndex++;
}
pointsIt++;
- idx += 1;
}
if (nextContour->GetClosed())
{
vtkIdType cell[2] = {lastPointIndex, firstPointIndex};
lines->InsertNextCell((vtkIdType)2, cell);
}
contourIt++;
}
m_ContourSet->SetPoints(points);
m_ContourSet->SetLines(lines);
m_TubeFilter->SetInputData(m_ContourSet);
m_TubeFilter->SetRadius(1);
m_TubeFilter->Update();
m_VtkPolyDataMapper->SetInputConnection(m_TubeFilter->GetOutputPort());
double rgba[4] = {0.0f, 1.0f, 0.0f, 0.6f};
m_Actor->GetProperty()->SetColor(rgba);
m_Actor->SetMapper(m_VtkPolyDataMapper);
}
}
const mitk::ContourSet *mitk::ContourSetVtkMapper3D::GetInput()
{
return static_cast<const mitk::ContourSet *>(GetDataNode()->GetData());
}
diff --git a/Modules/Segmentation/Resources/AI_48x48.png b/Modules/Segmentation/Resources/AI_48x48.png
new file mode 100644
index 0000000000..9e546998a8
Binary files /dev/null and b/Modules/Segmentation/Resources/AI_48x48.png differ
diff --git a/Modules/Segmentation/Resources/AI_Cursor_32x32.png b/Modules/Segmentation/Resources/AI_Cursor_32x32.png
new file mode 100644
index 0000000000..fc16240c92
Binary files /dev/null and b/Modules/Segmentation/Resources/AI_Cursor_32x32.png differ
diff --git a/Modules/Segmentation/Resources/Icons.svg b/Modules/Segmentation/Resources/Icons.svg
index de7cdddc75..1404173765 100644
--- a/Modules/Segmentation/Resources/Icons.svg
+++ b/Modules/Segmentation/Resources/Icons.svg
@@ -1,1075 +1,1433 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="744.09448819"
- height="1052.3622047"
- id="svg2985"
+ sodipodi:docname="Icons.svg"
+ inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
version="1.1"
- inkscape:version="0.48.4 r9939"
- sodipodi:docname="Icons.svg">
+ id="svg2985"
+ height="1052.3622047"
+ width="744.09448819">
<defs
id="defs2987">
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible"
+ id="TriangleOutS"
+ refX="0.0"
+ refY="0.0"
+ orient="auto"
+ inkscape:stockid="TriangleOutS">
+ <path
+ transform="scale(0.2)"
+ style="fill-rule:evenodd;stroke:#00adff;stroke-width:1pt;stroke-opacity:1;fill:#00adff;fill-opacity:1"
+ d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
+ id="path1234" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible"
+ id="TriangleInS"
+ refX="0.0"
+ refY="0.0"
+ orient="auto"
+ inkscape:stockid="TriangleInS">
+ <path
+ transform="scale(-0.2)"
+ style="fill-rule:evenodd;stroke:#00adff;stroke-width:1pt;stroke-opacity:1;fill:#00adff;fill-opacity:1"
+ d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
+ id="path1225" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible;"
+ id="Arrow2Mend"
+ refX="0.0"
+ refY="0.0"
+ orient="auto"
+ inkscape:stockid="Arrow2Mend">
+ <path
+ transform="scale(0.6) rotate(180) translate(0,0)"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#00adff;stroke-opacity:1;fill:#00adff;fill-opacity:1"
+ id="path1113" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible;"
+ id="Arrow2Lend"
+ refX="0.0"
+ refY="0.0"
+ orient="auto"
+ inkscape:stockid="Arrow2Lend">
+ <path
+ transform="scale(1.1) rotate(180) translate(1,0)"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#00adff;stroke-opacity:1;fill:#00adff;fill-opacity:1"
+ id="path1107" />
+ </marker>
<linearGradient
id="linearGradient3918">
<stop
- id="stop3920"
+ style="stop-color:#c0c0c0;stop-opacity:1;"
offset="0"
- style="stop-color:#c0c0c0;stop-opacity:1;" />
+ id="stop3920" />
<stop
- id="stop3922"
+ style="stop-color:#808080;stop-opacity:0"
offset="1"
- style="stop-color:#808080;stop-opacity:0" />
+ id="stop3922" />
</linearGradient>
<linearGradient
id="linearGradient3902">
<stop
- style="stop-color:#c0c0c0;stop-opacity:1;"
+ id="stop3904"
offset="0"
- id="stop3904" />
+ style="stop-color:#c0c0c0;stop-opacity:1;" />
<stop
- style="stop-color:#808080;stop-opacity:1;"
+ id="stop3906"
offset="1"
- id="stop3906" />
+ style="stop-color:#808080;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient3894">
<stop
- style="stop-color:#c0c0a2;stop-opacity:1;"
+ id="stop3896"
offset="0"
- id="stop3896" />
+ style="stop-color:#c0c0a2;stop-opacity:1;" />
<stop
- style="stop-color:#000000;stop-opacity:1;"
+ id="stop3898"
offset="1"
- id="stop3898" />
+ style="stop-color:#000000;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient5120">
<stop
- style="stop-color:#00a6f4;stop-opacity:1;"
+ id="stop5122"
offset="0"
- id="stop5122" />
+ style="stop-color:#00a6f4;stop-opacity:1;" />
<stop
- style="stop-color:#00618e;stop-opacity:1;"
+ id="stop5124"
offset="1"
- id="stop5124" />
+ style="stop-color:#00618e;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient5112">
<stop
- style="stop-color:#00adff;stop-opacity:1;"
+ id="stop5114"
offset="0"
- id="stop5114" />
+ style="stop-color:#00adff;stop-opacity:1;" />
<stop
- style="stop-color:#bfebff;stop-opacity:1;"
+ id="stop5116"
offset="1"
- id="stop5116" />
+ style="stop-color:#bfebff;stop-opacity:1;" />
</linearGradient>
<inkscape:path-effect
- effect="spiro"
+ is_visible="true"
id="path-effect5017"
- is_visible="true" />
+ effect="spiro" />
<inkscape:path-effect
- effect="spiro"
+ is_visible="true"
id="path-effect5007"
- is_visible="true" />
+ effect="spiro" />
<inkscape:path-effect
- effect="spiro"
+ is_visible="true"
id="path-effect5003"
- is_visible="true" />
+ effect="spiro" />
<inkscape:path-effect
- effect="spiro"
+ is_visible="true"
id="path-effect4938"
- is_visible="true" />
+ effect="spiro" />
<linearGradient
id="linearGradient4928">
<stop
- id="stop4930"
+ style="stop-color:#f0a0a0;stop-opacity:1;"
offset="0"
- style="stop-color:#f0a0a0;stop-opacity:1;" />
+ id="stop4930" />
<stop
- id="stop4932"
+ style="stop-color:#f0a0a0;stop-opacity:1;"
offset="1"
- style="stop-color:#f0a0a0;stop-opacity:1;" />
+ id="stop4932" />
</linearGradient>
<inkscape:path-effect
- effect="spiro"
+ is_visible="true"
id="path-effect4914"
- is_visible="true" />
+ effect="spiro" />
<linearGradient
id="linearGradient4826">
<stop
- style="stop-color:#f0a0a0;stop-opacity:1;"
+ id="stop4828"
offset="0"
- id="stop4828" />
+ style="stop-color:#f0a0a0;stop-opacity:1;" />
<stop
- style="stop-color:#c86464;stop-opacity:0;"
+ id="stop4830"
offset="1"
- id="stop4830" />
+ style="stop-color:#c86464;stop-opacity:0;" />
</linearGradient>
<linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient5112"
- id="linearGradient5141"
- gradientUnits="userSpaceOnUse"
- x1="70.3125"
- y1="413.84039"
+ y2="406.96539"
x2="66.868843"
- y2="406.96539" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient5120"
- id="linearGradient5143"
+ y1="413.84039"
+ x1="70.3125"
gradientUnits="userSpaceOnUse"
- x1="61.28059"
- y1="451.38803"
- x2="50.8452"
- y2="451.70779" />
+ id="linearGradient5141"
+ xlink:href="#linearGradient5112"
+ inkscape:collect="always" />
<linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient4826"
- id="linearGradient3095"
+ y2="451.70779"
+ x2="50.8452"
+ y1="451.38803"
+ x1="61.28059"
gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(-0.70439223,-0.70981095,0.70981095,-0.70439223,-164.03247,785.42253)"
- x1="83.578606"
- y1="420.98395"
- x2="74.169212"
- y2="434.27652" />
+ id="linearGradient5143"
+ xlink:href="#linearGradient5120"
+ inkscape:collect="always" />
<linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient3902"
- id="linearGradient3908"
- x1="-139.41162"
- y1="978.08923"
- x2="-140.94791"
- y2="1004.5432"
+ y2="434.27652"
+ x2="74.169212"
+ y1="420.98395"
+ x1="83.578606"
+ gradientTransform="matrix(-0.70439223,-0.70981095,0.70981095,-0.70439223,-164.03247,785.42253)"
gradientUnits="userSpaceOnUse"
- gradientTransform="translate(-64.484689,14.609691)" />
+ id="linearGradient3095"
+ xlink:href="#linearGradient4826"
+ inkscape:collect="always" />
<linearGradient
- inkscape:collect="always"
+ gradientTransform="translate(-64.484689,14.609691)"
+ gradientUnits="userSpaceOnUse"
+ y2="1004.5432"
+ x2="-140.94791"
+ y1="978.08923"
+ x1="-139.41162"
+ id="linearGradient3908"
xlink:href="#linearGradient3902"
- id="linearGradient3916"
- x1="-152.49144"
- y1="969.72015"
- x2="-150.91258"
- y2="983.25476"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="matrix(1.3445523,0,0,1.6941179,-12.57905,-659.41683)"
gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(1.3445523,0,0,1.6941179,-12.57905,-659.41683)" />
+ y2="983.25476"
+ x2="-150.91258"
+ y1="969.72015"
+ x1="-152.49144"
+ id="linearGradient3916"
+ xlink:href="#linearGradient3902"
+ inkscape:collect="always" />
+ <marker
+ inkscape:stockid="TriangleOutS"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="TriangleOutS-3"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path1234-7"
+ d="M 5.77,0 -2.88,5 V -5 Z"
+ style="fill:#00adff;fill-opacity:1;fill-rule:evenodd;stroke:#00adff;stroke-width:1pt;stroke-opacity:1"
+ transform="scale(0.2)" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible"
+ id="TriangleOutS-3-9"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="TriangleOutS">
+ <path
+ transform="scale(0.2)"
+ style="fill:#00adff;fill-opacity:1;fill-rule:evenodd;stroke:#00adff;stroke-width:1pt;stroke-opacity:1"
+ d="M 5.77,0 -2.88,5 V -5 Z"
+ id="path1234-7-6" />
+ </marker>
+ <marker
+ inkscape:stockid="TriangleOutS"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="TriangleOutS-3-9-0"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path1234-7-6-6"
+ d="M 5.77,0 -2.88,5 V -5 Z"
+ style="fill:#00adff;fill-opacity:1;fill-rule:evenodd;stroke:#00adff;stroke-width:1pt;stroke-opacity:1"
+ transform="scale(0.2)" />
+ </marker>
+ <marker
+ inkscape:stockid="TriangleOutS"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="TriangleOutS-6"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path1234-2"
+ d="M 5.77,0 -2.88,5 V -5 Z"
+ style="fill:#00adff;fill-opacity:1;fill-rule:evenodd;stroke:#00adff;stroke-width:1pt;stroke-opacity:1"
+ transform="scale(0.2)" />
+ </marker>
+ <marker
+ inkscape:stockid="TriangleOutS"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="TriangleOutS-8"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path1234-1"
+ d="M 5.77,0 -2.88,5 V -5 Z"
+ style="fill:#00adff;fill-opacity:1;fill-rule:evenodd;stroke:#00adff;stroke-width:1pt;stroke-opacity:1"
+ transform="scale(0.2)" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible"
+ id="TriangleOutS-8-8"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="TriangleOutS">
+ <path
+ transform="scale(0.2)"
+ style="fill:#00adff;fill-opacity:1;fill-rule:evenodd;stroke:#00adff;stroke-width:1pt;stroke-opacity:1"
+ d="M 5.77,0 -2.88,5 V -5 Z"
+ id="path1234-1-1" />
+ </marker>
+ <marker
+ inkscape:stockid="TriangleOutS"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="TriangleOutS-0"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path1234-6"
+ d="M 5.77,0 -2.88,5 V -5 Z"
+ style="fill:#00adff;fill-opacity:1;fill-rule:evenodd;stroke:#00adff;stroke-width:1pt;stroke-opacity:1"
+ transform="scale(0.2)" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible"
+ id="TriangleOutS-0-7"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="TriangleOutS">
+ <path
+ transform="scale(0.2)"
+ style="fill:#00adff;fill-opacity:1;fill-rule:evenodd;stroke:#00adff;stroke-width:1pt;stroke-opacity:1"
+ d="M 5.77,0 -2.88,5 V -5 Z"
+ id="path1234-6-8" />
+ </marker>
+ <marker
+ inkscape:stockid="TriangleOutS"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="TriangleOutS-8-8-2"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path1234-1-1-1"
+ d="M 5.77,0 -2.88,5 V -5 Z"
+ style="fill:#00adff;fill-opacity:1;fill-rule:evenodd;stroke:#00adff;stroke-width:1pt;stroke-opacity:1"
+ transform="scale(0.2)" />
+ </marker>
+ <marker
+ inkscape:stockid="TriangleOutS"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="TriangleOutS-4"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path1234-8"
+ d="M 5.77,0 -2.88,5 V -5 Z"
+ style="fill:#00adff;fill-opacity:1;fill-rule:evenodd;stroke:#00adff;stroke-width:1pt;stroke-opacity:1"
+ transform="scale(0.2)" />
+ </marker>
+ <marker
+ inkscape:stockid="TriangleOutS"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="TriangleOutS-1"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path1234-9"
+ d="M 5.77,0 -2.88,5 V -5 Z"
+ style="fill:#00adff;fill-opacity:1;fill-rule:evenodd;stroke:#00adff;stroke-width:1pt;stroke-opacity:1"
+ transform="scale(0.2)" />
+ </marker>
</defs>
<sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#ffffff"
- borderopacity="0.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="0"
- inkscape:zoom="3.4481328"
- inkscape:cx="-55.659347"
- inkscape:cy="-4.3610416"
- inkscape:document-units="px"
- inkscape:current-layer="layer14"
- showgrid="false"
- inkscape:window-width="1920"
- inkscape:window-height="1018"
- inkscape:window-x="-8"
+ inkscape:document-rotation="0"
+ inkscape:window-maximized="1"
inkscape:window-y="-8"
- inkscape:window-maximized="1" />
+ inkscape:window-x="-8"
+ inkscape:window-height="1377"
+ inkscape:window-width="2560"
+ showgrid="false"
+ inkscape:current-layer="layer16"
+ inkscape:document-units="px"
+ inkscape:cy="1006.5185"
+ inkscape:cx="32.176472"
+ inkscape:zoom="4.8763962"
+ inkscape:pageshadow="0"
+ inkscape:pageopacity="0.0"
+ borderopacity="0.0"
+ bordercolor="#ffffff"
+ pagecolor="#ffffff"
+ id="base" />
<metadata
id="metadata2990">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
+ <dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
- inkscape:groupmode="layer"
- id="layer2"
- inkscape:label="Background"
+ sodipodi:insensitive="true"
style="display:inline"
- sodipodi:insensitive="true">
+ inkscape:label="Background"
+ id="layer2"
+ inkscape:groupmode="layer">
<rect
- y="967.95813"
- x="-67.125"
- height="92.279053"
- width="158.99408"
+ style="fill:#dddddd;fill-opacity:1;stroke:none;stroke-width:1"
id="rect4283-8"
- style="fill:#dddddd;fill-opacity:1;stroke:none" />
+ width="152.63693"
+ height="127.34593"
+ x="-67.125"
+ y="967.95813" />
</g>
<g
- inkscape:groupmode="layer"
- id="layer3"
+ sodipodi:insensitive="true"
inkscape:label="Add"
- sodipodi:insensitive="true">
+ id="layer3"
+ inkscape:groupmode="layer">
<g
- id="g4949"
- transform="translate(-179.1281,582.80863)">
+ transform="translate(-179.1281,582.80863)"
+ id="g4949">
<g
- transform="translate(-78.5,-7)"
- id="g3808">
+ id="g3808"
+ transform="translate(-78.5,-7)">
<path
- style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m 265.125,415.73718 c 2.66059,2.30475 8.06002,1.41215 10.5,-1.125 1.70795,-1.77597 2.30703,-5.69994 0.5,-7.375 -2.77871,-2.57579 -8.82218,-1.28388 -11.25,1.625 -1.4694,1.76055 -1.48328,5.37353 0.25,6.875 z"
- id="path3004"
+ sodipodi:nodetypes="aaaaa"
inkscape:connector-curvature="0"
- sodipodi:nodetypes="aaaaa" />
+ id="path3004"
+ d="m 265.125,415.73718 c 2.66059,2.30475 8.06002,1.41215 10.5,-1.125 1.70795,-1.77597 2.30703,-5.69994 0.5,-7.375 -2.77871,-2.57579 -8.82218,-1.28388 -11.25,1.625 -1.4694,1.76055 -1.48328,5.37353 0.25,6.875 z"
+ style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
- style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m 260.625,425.73718 c 6.84332,-2.29563 3.78655,-5.82877 5.625,-8.75"
- id="path3774"
+ sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
- sodipodi:nodetypes="cc" />
- <path
- sodipodi:type="arc"
- style="fill:#00adff;fill-opacity:1;stroke:none"
+ id="path3774"
+ d="m 260.625,425.73718 c 6.84332,-2.29563 3.78655,-5.82877 5.625,-8.75"
+ style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <circle
+ r="1.625"
+ cy="396.11218"
+ cx="245.375"
+ transform="matrix(1.7077889,0.59262847,-0.4791464,1.3807655,37.309511,-275.74206)"
id="path3776"
- sodipodi:cx="245.375"
- sodipodi:cy="396.11218"
- sodipodi:rx="1.625"
- sodipodi:ry="1.625"
- d="m 247,396.11218 c 0,0.89747 -0.72754,1.625 -1.625,1.625 -0.89746,0 -1.625,-0.72753 -1.625,-1.625 0,-0.89746 0.72754,-1.625 1.625,-1.625 0.89746,0 1.625,0.72754 1.625,1.625 z"
- transform="matrix(1.7077889,0.59262847,-0.4791464,1.3807655,37.309511,-275.74206)" />
+ style="fill:#00adff;fill-opacity:1;stroke:none" />
</g>
<g
- id="g3802"
- transform="translate(-73,-8.625)">
+ transform="translate(-73,-8.625)"
+ id="g3802">
<path
- style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m 271.75,420.48718 c 0,7.25 0,7.375 0,7.375"
+ inkscape:connector-curvature="0"
id="path3778"
- inkscape:connector-curvature="0" />
+ d="m 271.75,420.48718 c 0,7.25 0,7.375 0,7.375"
+ style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
- style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m 268.0625,424.11218 c 7.25,0 7.375,0 7.375,0"
+ inkscape:connector-curvature="0"
id="path3778-1"
- inkscape:connector-curvature="0" />
+ d="m 268.0625,424.11218 c 7.25,0 7.375,0 7.375,0"
+ style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</g>
</g>
<g
- inkscape:groupmode="layer"
- id="layer4"
- inkscape:label="Subtract"
- sodipodi:insensitive="true">
+ sodipodi:insensitive="true"
+ inkscape:label="Polygonal Add"
+ id="layer15"
+ inkscape:groupmode="layer">
<g
- id="g4958"
- transform="translate(-125.47497,582.44124)">
+ transform="translate(-175.4023,646.62933)"
+ id="g3256">
<g
- transform="translate(-105.18125,-6.3826041)"
- id="g3808-1">
+ transform="translate(-138.24248,-21.500602)"
+ id="g3808-66"
+ inkscape:export-xdpi="136.48447"
+ inkscape:export-ydpi="136.48447">
<path
- style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 265.125,415.73718 c 2.66059,2.30475 8.06002,1.41215 10.5,-1.125 1.70795,-1.77597 2.30703,-5.69994 0.5,-7.375 -2.77871,-2.57579 -8.82218,-1.28388 -11.25,1.625 -1.4694,1.76055 -1.48328,5.37353 0.25,6.875 z"
- id="path3004-6"
+ id="path3004-1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="aaaaa" />
<path
- style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 260.625,425.73718 c 6.84332,-2.29563 3.78655,-5.82877 5.625,-8.75"
- id="path3774-5"
+ id="path3774-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
- <path
- sodipodi:type="arc"
+ <circle
style="fill:#00adff;fill-opacity:1;stroke:none"
+ id="path3776-2"
+ transform="matrix(1.7077889,0.59262847,-0.4791464,1.3807655,37.309511,-275.74206)"
+ cx="245.375"
+ cy="396.11218"
+ r="1.625" />
+ </g>
+ <g
+ style="fill:none;fill-opacity:1;stroke:#00adff;stroke-width:1.49312;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g3802-8"
+ transform="matrix(0.9997009,0.0937092,-0.0940838,1.000731,-95.457299,-48.251875)"
+ inkscape:export-xdpi="136.48447"
+ inkscape:export-ydpi="136.48447">
+ <path
+ transform="matrix(0.34272987,-0.19126599,0.19009061,0.34062368,99.248259,325.57464)"
+ inkscape:transform-center-y="-0.67212892"
+ inkscape:transform-center-x="0.15131989"
+ inkscape:randomized="0"
+ inkscape:rounded="0"
+ inkscape:flatsided="true"
+ sodipodi:arg2="0.91112773"
+ sodipodi:arg1="0.2828092"
+ sodipodi:r2="2.2181654"
+ sodipodi:r1="12.492838"
+ sodipodi:cy="434.0499"
+ sodipodi:cx="267.46036"
+ sodipodi:sides="5"
+ id="path1029"
+ style="fill:none;fill-opacity:1;stroke:#00adff;stroke-width:3.81597;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="star"
+ d="m 279.45692,437.53608 -11.60498,9.00052 -12.14613,-8.25568 4.09825,-14.10281 14.67899,-0.46033 z" />
+ </g>
+ </g>
+ </g>
+ <g
+ sodipodi:insensitive="true"
+ inkscape:label="Subtract"
+ id="layer4"
+ inkscape:groupmode="layer">
+ <g
+ transform="translate(-125.47497,582.44124)"
+ id="g4958">
+ <g
+ id="g3808-1"
+ transform="translate(-105.18125,-6.3826041)">
+ <path
+ sodipodi:nodetypes="aaaaa"
+ inkscape:connector-curvature="0"
+ id="path3004-6"
+ d="m 265.125,415.73718 c 2.66059,2.30475 8.06002,1.41215 10.5,-1.125 1.70795,-1.77597 2.30703,-5.69994 0.5,-7.375 -2.77871,-2.57579 -8.82218,-1.28388 -11.25,1.625 -1.4694,1.76055 -1.48328,5.37353 0.25,6.875 z"
+ style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path3774-5"
+ d="m 260.625,425.73718 c 6.84332,-2.29563 3.78655,-5.82877 5.625,-8.75"
+ style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <circle
+ r="1.625"
+ cy="396.11218"
+ cx="245.375"
+ transform="matrix(1.7077889,0.59262847,-0.4791464,1.3807655,37.309511,-275.74206)"
id="path3776-7"
- sodipodi:cx="245.375"
- sodipodi:cy="396.11218"
- sodipodi:rx="1.625"
- sodipodi:ry="1.625"
- d="m 247,396.11218 c 0,0.89747 -0.72754,1.625 -1.625,1.625 -0.89746,0 -1.625,-0.72753 -1.625,-1.625 0,-0.89746 0.72754,-1.625 1.625,-1.625 0.89746,0 1.625,0.72754 1.625,1.625 z"
- transform="matrix(1.7077889,0.59262847,-0.4791464,1.3807655,37.309511,-275.74206)" />
+ style="fill:#00adff;fill-opacity:1;stroke:none" />
</g>
<g
- id="g3802-5"
- transform="translate(-99.68125,-8.0076041)">
+ transform="translate(-99.68125,-8.0076041)"
+ id="g3802-5">
<path
- style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m 268.0625,424.11218 c 7.25,0 7.375,0 7.375,0"
+ inkscape:connector-curvature="0"
id="path3778-1-1"
- inkscape:connector-curvature="0" />
+ d="m 268.0625,424.11218 c 7.25,0 7.375,0 7.375,0"
+ style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</g>
</g>
<g
- inkscape:groupmode="layer"
- id="layer5"
- inkscape:label="Paint"
+ sodipodi:insensitive="true"
style="display:inline"
- sodipodi:insensitive="true">
+ inkscape:label="Paint"
+ id="layer5"
+ inkscape:groupmode="layer">
<g
- id="g4812"
- transform="matrix(1.2341373,0,0,1.2341373,-90.543181,455.66052)">
+ transform="matrix(1.2341373,0,0,1.2341373,-90.543181,455.66052)"
+ id="g4812">
<g
- id="g4819"
- transform="translate(-13.808248,-18.277307)"
- inkscape:export-filename="C:\Users\Stefan\Desktop\paint48x48.png"
+ inkscape:export-ydpi="201.78"
inkscape:export-xdpi="201.78"
- inkscape:export-ydpi="201.78">
+ inkscape:export-filename="C:\Users\Stefan\Desktop\paint48x48.png"
+ transform="translate(-13.808248,-18.277307)"
+ id="g4819">
<g
- id="g4834"
- inkscape:export-filename="C:\Users\Stefan\Desktop\paint48x48.png"
- inkscape:export-xdpi="201.78"
+ transform="translate(56.719783,25.766987)"
inkscape:export-ydpi="201.78"
- transform="translate(56.719783,25.766987)">
+ inkscape:export-xdpi="201.78"
+ inkscape:export-filename="C:\Users\Stefan\Desktop\paint48x48.png"
+ id="g4834">
<g
- id="g3088"
- transform="translate(-0.20257042,-0.30385598)"
- inkscape:export-filename="C:\Users\Stefan\Desktop\Paint_48x48.png"
+ inkscape:export-ydpi="213.13113"
inkscape:export-xdpi="213.13113"
- inkscape:export-ydpi="213.13113">
+ inkscape:export-filename="C:\Users\Stefan\Desktop\Paint_48x48.png"
+ transform="translate(-0.20257042,-0.30385598)"
+ id="g3088">
<path
- style="fill:url(#linearGradient3095);fill-opacity:1;stroke:none"
- d="m 76.945752,433.09508 c 1.43564,1.55145 4.33834,1.94291 6.25392,1.04927 2.31171,-1.07842 1.87769,-4.16238 3.87904,-6.59663 0.91842,-1.11709 5.36944,-0.5283 4.14155,-1.29229 -2.13201,-1.32653 -3.75415,-2.38531 -6.68197,-0.061 -1.85345,1.47143 -1.91442,3.38562 -2.91402,3.87795 -1.92644,0.94881 -3.80913,-1.59069 -4.98357,-0.408 -0.80926,0.81435 -0.47467,2.58799 0.30505,3.43066 z"
- id="path4810"
+ sodipodi:nodetypes="aassssaa"
inkscape:connector-curvature="0"
- sodipodi:nodetypes="aassssaa" />
+ id="path4810"
+ d="m 76.945752,433.09508 c 1.43564,1.55145 4.33834,1.94291 6.25392,1.04927 2.31171,-1.07842 1.87769,-4.16238 3.87904,-6.59663 0.91842,-1.11709 5.36944,-0.5283 4.14155,-1.29229 -2.13201,-1.32653 -3.75415,-2.38531 -6.68197,-0.061 -1.85345,1.47143 -1.91442,3.38562 -2.91402,3.87795 -1.92644,0.94881 -3.80913,-1.59069 -4.98357,-0.408 -0.80926,0.81435 -0.47467,2.58799 0.30505,3.43066 z"
+ style="fill:url(#linearGradient3095);fill-opacity:1;stroke:none" />
<g
- id="g4805"
- transform="matrix(0.18227575,-1.0958452,1.0958452,0.18227575,-398.97255,439.13499)">
+ transform="matrix(0.18227575,-1.0958452,1.0958452,0.18227575,-398.97255,439.13499)"
+ id="g4805">
<path
- sodipodi:nodetypes="zszsz"
- inkscape:connector-curvature="0"
- id="path4799"
+ style="fill:#00adff;fill-opacity:1;stroke:none"
d="m 91.125,433.08718 c 0.210795,-0.27733 -2.148235,-2.8975 -4.6,-5.225 -1.846968,-1.75336 -5.223325,-5.47668 -6.1,-4.6 -0.876675,0.87668 3.124215,4.03746 5,5.75 2.378142,2.17118 5.489205,4.35233 5.7,4.075 z"
- style="fill:#00adff;fill-opacity:1;stroke:none" />
- <path
- sodipodi:nodetypes="csccc"
+ id="path4799"
inkscape:connector-curvature="0"
- id="path4803"
- d="m 79.95,423.98718 c 0,0 -1.568018,-0.57375 -1.65625,-1.29375 -0.167855,-1.36975 -0.66875,-2.28125 -0.66875,-2.28125 1.378623,0.43018 2.902816,0.56605 3.55,2.475 z"
- style="fill:#b60000;fill-opacity:1;stroke:none" />
+ sodipodi:nodetypes="zszsz" />
<path
- sodipodi:nodetypes="sssss"
+ style="fill:#b60000;fill-opacity:1;stroke:none"
+ d="m 79.95,423.98718 c 0,0 -1.568018,-0.57375 -1.65625,-1.29375 -0.167855,-1.36975 -0.66875,-2.28125 -0.66875,-2.28125 1.378623,0.43018 2.902816,0.56605 3.55,2.475 z"
+ id="path4803"
inkscape:connector-curvature="0"
- id="path4801"
+ sodipodi:nodetypes="csccc" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 79.8375,423.78718 c 0.336564,-0.36709 0.839135,-0.8625 1.15,-1.15 0.203325,-0.18804 0.606548,0.49186 0.5875,0.5875 -0.107145,0.53795 -0.582122,0.97262 -1.1125,1.2125 -0.124211,0.0562 -0.82813,-0.42845 -0.625,-0.65 z"
- style="fill:#ffffff;fill-opacity:1;stroke:none" />
+ id="path4801"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="sssss" />
</g>
</g>
</g>
</g>
</g>
</g>
<g
- inkscape:groupmode="layer"
- id="layer6"
- inkscape:label="Wipe"
sodipodi:insensitive="true"
- style="display:inline">
+ style="display:inline"
+ inkscape:label="Wipe"
+ id="layer6"
+ inkscape:groupmode="layer">
<g
- id="g4941"
- transform="translate(-41.378098,573.18881)"
- inkscape:export-filename="C:\Users\Stefan\Desktop\wipe48x48.png"
+ inkscape:export-ydpi="198.05157"
inkscape:export-xdpi="198.05157"
- inkscape:export-ydpi="198.05157">
+ inkscape:export-filename="C:\Users\Stefan\Desktop\wipe48x48.png"
+ transform="translate(-41.378098,573.18881)"
+ id="g4941">
<path
- id="path4916"
- d="m 51.71875,445.15625 c -4.646313,0.4773 -8.09375,2.10246 -8.09375,4.03125 0,0.82413 0.640987,1.60303 1.71875,2.25 1.080492,-0.88864 2.86372,-1.97552 3.9375,-2.875 0.957841,-0.80237 1.413303,-2.55797 2.4375,-3.40625 z m 7.375,0.25 c 0.506128,0.36537 0.899418,0.84905 1.09375,1.4375 0.51831,1.56947 -1.721297,2.95399 -3.09375,3.875 -1.937856,1.30043 -4.407796,1.64334 -6.71875,1.96875 -0.311289,0.0438 -0.714852,0.0735 -1.09375,0.125 1.563527,0.33169 3.335671,0.5625 5.25,0.5625 6.019904,0 10.90625,-1.88368 10.90625,-4.1875 0,-1.67908 -2.607273,-3.11974 -6.34375,-3.78125 z"
+ inkscape:connector-curvature="0"
style="fill:#f0a0a0;fill-opacity:1;stroke:none"
- inkscape:connector-curvature="0" />
+ d="m 51.71875,445.15625 c -4.646313,0.4773 -8.09375,2.10246 -8.09375,4.03125 0,0.82413 0.640987,1.60303 1.71875,2.25 1.080492,-0.88864 2.86372,-1.97552 3.9375,-2.875 0.957841,-0.80237 1.413303,-2.55797 2.4375,-3.40625 z m 7.375,0.25 c 0.506128,0.36537 0.899418,0.84905 1.09375,1.4375 0.51831,1.56947 -1.721297,2.95399 -3.09375,3.875 -1.937856,1.30043 -4.407796,1.64334 -6.71875,1.96875 -0.311289,0.0438 -0.714852,0.0735 -1.09375,0.125 1.563527,0.33169 3.335671,0.5625 5.25,0.5625 6.019904,0 10.90625,-1.88368 10.90625,-4.1875 0,-1.67908 -2.607273,-3.11974 -6.34375,-3.78125 z"
+ id="path4916" />
<g
- transform="matrix(0.93314753,0.35949366,-0.35949366,0.93314753,166.03247,8.0201654)"
- id="g4904">
+ id="g4904"
+ transform="rotate(21.069103,61.452343,450.42305)">
<g
id="g4900">
<rect
- y="444.79077"
- x="50.114285"
- height="2.8857133"
- width="9.1428576"
+ style="fill:#ffffff;fill-opacity:1;stroke:none"
id="rect4841"
- style="fill:#ffffff;fill-opacity:1;stroke:none" />
- <path
- transform="translate(-0.01075506,-2.8285714)"
- d="m 59.250078,450.43362 c 0,1.55429 -2.03872,2.81429 -4.55361,2.81429 -2.514889,0 -4.553609,-1.26 -4.553609,-2.81429 0,-1.55428 2.03872,-2.81428 4.553609,-2.81428 2.51489,0 4.55361,1.26 4.55361,2.81428 z"
- sodipodi:ry="2.8142858"
- sodipodi:rx="4.5536098"
- sodipodi:cy="450.43362"
- sodipodi:cx="54.696468"
- id="path4843"
+ width="9.1428576"
+ height="2.8857133"
+ x="50.114285"
+ y="444.79077" />
+ <ellipse
+ ry="2.8142858"
+ rx="4.5536098"
+ cy="450.43362"
+ cx="54.696468"
style="fill:#ffffff;fill-opacity:1;stroke:none"
- sodipodi:type="arc" />
+ id="path4843"
+ transform="translate(-0.01075506,-2.8285714)" />
</g>
<rect
- style="fill:#00adff;fill-opacity:1;stroke:none"
- id="rect4841-2"
- width="9.1428576"
- height="9.0285702"
+ y="435.77646"
x="50.114285"
- y="435.77646" />
+ height="9.0285702"
+ width="9.1428576"
+ id="rect4841-2"
+ style="fill:#00adff;fill-opacity:1;stroke:none" />
</g>
</g>
</g>
<g
- inkscape:groupmode="layer"
- id="layer7"
+ sodipodi:insensitive="true"
+ style="display:inline"
inkscape:label="Region Growing"
- sodipodi:insensitive="true">
+ id="layer7"
+ inkscape:groupmode="layer">
<g
- transform="translate(-96.62711,569.18521)"
- inkscape:export-ydpi="222.95746"
- inkscape:export-xdpi="222.95746"
+ id="g4342"
inkscape:export-filename="C:\Users\Stefan\Desktop\regiongrowing48x48.png"
- id="g4342">
+ inkscape:export-xdpi="222.95746"
+ inkscape:export-ydpi="222.95746"
+ transform="translate(-96.62711,569.18521)">
<g
- id="g4311"
- transform="matrix(0.7415098,0.67094204,-0.67094204,0.7415098,341.75029,21.737459)">
+ transform="rotate(42.139813,142.66406,454.39548)"
+ id="g4311">
<rect
- y="452.19128"
- x="131.19289"
- height="2"
- width="14.3"
+ style="fill:#00adff;fill-opacity:1;stroke:none"
id="rect4291"
- style="fill:#00adff;fill-opacity:1;stroke:none" />
- <rect
- y="452.19128"
- x="128.57915"
+ width="14.3"
height="2"
- width="2.6327384"
- id="rect4291-9"
- style="fill:#ffffff;fill-opacity:1;stroke:none" />
+ x="131.19289"
+ y="452.19128" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none"
+ id="rect4291-9"
+ width="2.6327384"
+ height="2"
+ x="128.57915"
+ y="452.19128" />
</g>
<path
- sodipodi:type="star"
- style="fill:#f0a0a0;fill-opacity:1;stroke:none"
- id="path4315"
- sodipodi:sides="5"
- sodipodi:cx="127.42064"
- sodipodi:cy="438.31064"
- sodipodi:r1="2.0124612"
- sodipodi:r2="1.0062306"
- sodipodi:arg1="1.2490458"
- sodipodi:arg2="1.8773643"
- inkscape:flatsided="false"
- inkscape:rounded="0"
- inkscape:randomized="0"
- d="m 128.05704,440.21983 -0.94007,-0.94988 -1.31542,0.23591 0.61289,-1.18758 -0.63085,-1.17814 1.31885,0.2159 0.92554,-0.96403 0.2022,1.32102 1.20286,0.58233 -1.19388,0.60053 z"
- inkscape:transform-center-x="-0.1876776"
+ transform="translate(4.6669048,0.77781746)"
inkscape:transform-center-y="-0.0047208612"
- transform="translate(4.6669048,0.77781746)" />
- <path
- sodipodi:type="star"
- style="fill:#f0a0a0;fill-opacity:1;stroke:none"
- id="path4315-3"
- sodipodi:sides="5"
- sodipodi:cx="127.42064"
- sodipodi:cy="438.31064"
- sodipodi:r1="2.0124612"
- sodipodi:r2="1.0062306"
- sodipodi:arg1="1.2490458"
- sodipodi:arg2="1.8773643"
- inkscape:flatsided="false"
- inkscape:rounded="0"
- inkscape:randomized="0"
+ inkscape:transform-center-x="-0.1876776"
d="m 128.05704,440.21983 -0.94007,-0.94988 -1.31542,0.23591 0.61289,-1.18758 -0.63085,-1.17814 1.31885,0.2159 0.92554,-0.96403 0.2022,1.32102 1.20286,0.58233 -1.19388,0.60053 z"
- inkscape:transform-center-x="0.12386089"
- inkscape:transform-center-y="0.049309956"
- transform="matrix(0.67312163,-0.59018349,0.57650582,0.68909152,-208.12989,221.84494)" />
- <path
- sodipodi:type="star"
- style="fill:#f0a0a0;fill-opacity:1;stroke:none"
- id="path4315-4"
- sodipodi:sides="5"
- sodipodi:cx="127.42064"
- sodipodi:cy="438.31064"
- sodipodi:r1="2.0124612"
- sodipodi:r2="1.0062306"
- sodipodi:arg1="1.2490458"
- sodipodi:arg2="1.8773643"
- inkscape:flatsided="false"
- inkscape:rounded="0"
inkscape:randomized="0"
+ inkscape:rounded="0"
+ inkscape:flatsided="false"
+ sodipodi:arg2="1.8773643"
+ sodipodi:arg1="1.2490458"
+ sodipodi:r2="1.0062306"
+ sodipodi:r1="2.0124612"
+ sodipodi:cy="438.31064"
+ sodipodi:cx="127.42064"
+ sodipodi:sides="5"
+ id="path4315"
+ style="fill:#f0a0a0;fill-opacity:1;stroke:none"
+ sodipodi:type="star" />
+ <path
+ transform="matrix(0.67312163,-0.59018349,0.57650582,0.68909152,-208.12989,221.84494)"
+ inkscape:transform-center-y="0.049309956"
+ inkscape:transform-center-x="0.12386089"
d="m 128.05704,440.21983 -0.94007,-0.94988 -1.31542,0.23591 0.61289,-1.18758 -0.63085,-1.17814 1.31885,0.2159 0.92554,-0.96403 0.2022,1.32102 1.20286,0.58233 -1.19388,0.60053 z"
- inkscape:transform-center-x="0.045280212"
+ inkscape:randomized="0"
+ inkscape:rounded="0"
+ inkscape:flatsided="false"
+ sodipodi:arg2="1.8773643"
+ sodipodi:arg1="1.2490458"
+ sodipodi:r2="1.0062306"
+ sodipodi:r1="2.0124612"
+ sodipodi:cy="438.31064"
+ sodipodi:cx="127.42064"
+ sodipodi:sides="5"
+ id="path4315-3"
+ style="fill:#f0a0a0;fill-opacity:1;stroke:none"
+ sodipodi:type="star" />
+ <path
+ transform="rotate(-49.389213,133.07208,439.20054)"
inkscape:transform-center-y="0.14860098"
- transform="matrix(0.65091716,-0.75914877,0.75914877,0.65091716,-286.96537,254.33888)" />
+ inkscape:transform-center-x="0.045280212"
+ d="m 128.05704,440.21983 -0.94007,-0.94988 -1.31542,0.23591 0.61289,-1.18758 -0.63085,-1.17814 1.31885,0.2159 0.92554,-0.96403 0.2022,1.32102 1.20286,0.58233 -1.19388,0.60053 z"
+ inkscape:randomized="0"
+ inkscape:rounded="0"
+ inkscape:flatsided="false"
+ sodipodi:arg2="1.8773643"
+ sodipodi:arg1="1.2490458"
+ sodipodi:r2="1.0062306"
+ sodipodi:r1="2.0124612"
+ sodipodi:cy="438.31064"
+ sodipodi:cx="127.42064"
+ sodipodi:sides="5"
+ id="path4315-4"
+ style="fill:#f0a0a0;fill-opacity:1;stroke:none"
+ sodipodi:type="star" />
</g>
+ <g
+ transform="translate(-49.025446,-7.6390964)"
+ id="g2362" />
</g>
<g
- inkscape:label="Fast Marching"
- id="g3095"
+ sodipodi:insensitive="true"
+ style="display:inline"
inkscape:groupmode="layer"
- sodipodi:insensitive="true">
+ id="g3095"
+ inkscape:label="Fast Marching">
<g
- id="g3883"
+ inkscape:export-ydpi="221.8"
+ inkscape:export-xdpi="221.8"
+ inkscape:export-filename="D:\MITK\Modules\Segmentation\resources\FastMarching_48x48.png"
transform="translate(-0.75,0.375)"
+ id="g3883">
+ <g
+ id="g3097"
+ inkscape:export-filename="C:\Users\Stefan\Desktop\regiongrowing48x48.png"
+ inkscape:export-xdpi="222.95746"
+ inkscape:export-ydpi="222.95746"
+ transform="translate(-149.12711,592.43521)">
+ <g
+ transform="rotate(42.139813,142.66406,454.39548)"
+ id="g3099">
+ <rect
+ style="fill:#00adff;fill-opacity:1;stroke:none"
+ id="rect3101"
+ width="14.3"
+ height="2"
+ x="131.19289"
+ y="452.19128" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none"
+ id="rect3103"
+ width="2.6327384"
+ height="2"
+ x="128.57915"
+ y="452.19128" />
+ </g>
+ <path
+ transform="translate(4.6669048,0.77781746)"
+ inkscape:transform-center-y="-0.0047208612"
+ inkscape:transform-center-x="-0.1876776"
+ d="m 128.05704,440.21983 -0.94007,-0.94988 -1.31542,0.23591 0.61289,-1.18758 -0.63085,-1.17814 1.31885,0.2159 0.92554,-0.96403 0.2022,1.32102 1.20286,0.58233 -1.19388,0.60053 z"
+ inkscape:randomized="0"
+ inkscape:rounded="0"
+ inkscape:flatsided="false"
+ sodipodi:arg2="1.8773643"
+ sodipodi:arg1="1.2490458"
+ sodipodi:r2="1.0062306"
+ sodipodi:r1="2.0124612"
+ sodipodi:cy="438.31064"
+ sodipodi:cx="127.42064"
+ sodipodi:sides="5"
+ id="path3105"
+ style="fill:#f0a0a0;fill-opacity:1;stroke:none"
+ sodipodi:type="star" />
+ <path
+ transform="matrix(0.67312163,-0.59018349,0.57650582,0.68909152,-208.12989,221.84494)"
+ inkscape:transform-center-y="0.049309956"
+ inkscape:transform-center-x="0.12386089"
+ d="m 128.05704,440.21983 -0.94007,-0.94988 -1.31542,0.23591 0.61289,-1.18758 -0.63085,-1.17814 1.31885,0.2159 0.92554,-0.96403 0.2022,1.32102 1.20286,0.58233 -1.19388,0.60053 z"
+ inkscape:randomized="0"
+ inkscape:rounded="0"
+ inkscape:flatsided="false"
+ sodipodi:arg2="1.8773643"
+ sodipodi:arg1="1.2490458"
+ sodipodi:r2="1.0062306"
+ sodipodi:r1="2.0124612"
+ sodipodi:cy="438.31064"
+ sodipodi:cx="127.42064"
+ sodipodi:sides="5"
+ id="path3107"
+ style="fill:#f0a0a0;fill-opacity:1;stroke:none"
+ sodipodi:type="star" />
+ <path
+ transform="rotate(-49.389213,133.07208,439.20054)"
+ inkscape:transform-center-y="0.14860098"
+ inkscape:transform-center-x="0.045280212"
+ d="m 128.05704,440.21983 -0.94007,-0.94988 -1.31542,0.23591 0.61289,-1.18758 -0.63085,-1.17814 1.31885,0.2159 0.92554,-0.96403 0.2022,1.32102 1.20286,0.58233 -1.19388,0.60053 z"
+ inkscape:randomized="0"
+ inkscape:rounded="0"
+ inkscape:flatsided="false"
+ sodipodi:arg2="1.8773643"
+ sodipodi:arg1="1.2490458"
+ sodipodi:r2="1.0062306"
+ sodipodi:r1="2.0124612"
+ sodipodi:cy="438.31064"
+ sodipodi:cx="127.42064"
+ sodipodi:sides="5"
+ id="path3109"
+ style="fill:#f0a0a0;fill-opacity:1;stroke:none"
+ sodipodi:type="star" />
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#00adff;fill-opacity:1;stroke:none"
+ x="-21.844007"
+ y="1048.7372"
+ id="text3111"><tspan
+ sodipodi:role="line"
+ id="tspan3113"
+ x="-21.844007"
+ y="1048.7372"
+ style="font-weight:bold;font-size:7.8843px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#00adff;fill-opacity:1;stroke:none">FM</tspan></text>
+ </g>
+ </g>
+ <g
+ sodipodi:insensitive="true"
+ style="display:inline"
+ inkscape:label="AI"
+ id="layer16"
+ inkscape:groupmode="layer">
+ <g
+ style="display:inline"
+ id="g3883-1"
+ transform="translate(-30.21137,26.595379)"
inkscape:export-filename="D:\MITK\Modules\Segmentation\resources\FastMarching_48x48.png"
- inkscape:export-xdpi="221.8"
- inkscape:export-ydpi="221.8">
+ inkscape:export-xdpi="230.74001"
+ inkscape:export-ydpi="230.74001">
<g
transform="translate(-149.12711,592.43521)"
inkscape:export-ydpi="222.95746"
inkscape:export-xdpi="222.95746"
inkscape:export-filename="C:\Users\Stefan\Desktop\regiongrowing48x48.png"
- id="g3097">
+ id="g3097-4">
<g
- id="g3099"
- transform="matrix(0.7415098,0.67094204,-0.67094204,0.7415098,341.75029,21.737459)">
+ id="g3099-6"
+ transform="rotate(42.139813,142.66406,454.39548)">
<rect
y="452.19128"
x="131.19289"
height="2"
width="14.3"
- id="rect3101"
+ id="rect3101-5"
style="fill:#00adff;fill-opacity:1;stroke:none" />
<rect
y="452.19128"
x="128.57915"
height="2"
width="2.6327384"
- id="rect3103"
+ id="rect3103-5"
style="fill:#ffffff;fill-opacity:1;stroke:none" />
</g>
<path
sodipodi:type="star"
style="fill:#f0a0a0;fill-opacity:1;stroke:none"
- id="path3105"
+ id="path3105-5"
sodipodi:sides="5"
sodipodi:cx="127.42064"
sodipodi:cy="438.31064"
sodipodi:r1="2.0124612"
sodipodi:r2="1.0062306"
sodipodi:arg1="1.2490458"
sodipodi:arg2="1.8773643"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 128.05704,440.21983 -0.94007,-0.94988 -1.31542,0.23591 0.61289,-1.18758 -0.63085,-1.17814 1.31885,0.2159 0.92554,-0.96403 0.2022,1.32102 1.20286,0.58233 -1.19388,0.60053 z"
inkscape:transform-center-x="-0.1876776"
inkscape:transform-center-y="-0.0047208612"
transform="translate(4.6669048,0.77781746)" />
<path
sodipodi:type="star"
style="fill:#f0a0a0;fill-opacity:1;stroke:none"
- id="path3107"
+ id="path3107-8"
sodipodi:sides="5"
sodipodi:cx="127.42064"
sodipodi:cy="438.31064"
sodipodi:r1="2.0124612"
sodipodi:r2="1.0062306"
sodipodi:arg1="1.2490458"
sodipodi:arg2="1.8773643"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 128.05704,440.21983 -0.94007,-0.94988 -1.31542,0.23591 0.61289,-1.18758 -0.63085,-1.17814 1.31885,0.2159 0.92554,-0.96403 0.2022,1.32102 1.20286,0.58233 -1.19388,0.60053 z"
inkscape:transform-center-x="0.12386089"
inkscape:transform-center-y="0.049309956"
transform="matrix(0.67312163,-0.59018349,0.57650582,0.68909152,-208.12989,221.84494)" />
<path
sodipodi:type="star"
style="fill:#f0a0a0;fill-opacity:1;stroke:none"
- id="path3109"
+ id="path3109-3"
sodipodi:sides="5"
sodipodi:cx="127.42064"
sodipodi:cy="438.31064"
sodipodi:r1="2.0124612"
sodipodi:r2="1.0062306"
sodipodi:arg1="1.2490458"
sodipodi:arg2="1.8773643"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 128.05704,440.21983 -0.94007,-0.94988 -1.31542,0.23591 0.61289,-1.18758 -0.63085,-1.17814 1.31885,0.2159 0.92554,-0.96403 0.2022,1.32102 1.20286,0.58233 -1.19388,0.60053 z"
inkscape:transform-center-x="0.045280212"
inkscape:transform-center-y="0.14860098"
- transform="matrix(0.65091716,-0.75914877,0.75914877,0.65091716,-286.96537,254.33888)" />
+ transform="rotate(-49.389213,133.07208,439.20054)" />
</g>
<text
- sodipodi:linespacing="125%"
- id="text3111"
+ id="text3111-2"
y="1048.7372"
x="-21.844007"
- style="font-size:31.53719139px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#00adff;fill-opacity:1;stroke:none;font-family:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#00adff;fill-opacity:1;stroke:none"
xml:space="preserve"><tspan
- style="font-size:7.88429785px;font-weight:bold;fill:#00adff;fill-opacity:1;stroke:none;-inkscape-font-specification:Sans Bold"
+ style="font-weight:bold;font-size:7.8843px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#00adff;fill-opacity:1;stroke:none"
y="1048.7372"
x="-21.844007"
- id="tspan3113"
- sodipodi:role="line">FM</tspan></text>
+ id="tspan3113-6"
+ sodipodi:role="line">AI</tspan></text>
</g>
</g>
<g
- inkscape:groupmode="layer"
- id="layer1"
+ sodipodi:insensitive="true"
inkscape:label="Threshold"
- sodipodi:insensitive="true">
+ id="layer1"
+ inkscape:groupmode="layer">
<g
id="g4034">
<path
- inkscape:export-ydpi="216"
- inkscape:export-xdpi="216"
- inkscape:export-filename="D:\MITK\Modules\Segmentation\resources\Threshold_48x48.png"
- inkscape:connector-curvature="0"
+ style="fill:#f0a0a0;fill-opacity:1;stroke:none"
+ d="m -17.715754,987.94743 c -1.597279,0.2543 -1.46135,6.3487 -3.28125,9.78122 -0.652424,1.2305 -1.528719,2.35895 -2.25,3.21885 v 1.0312 h 20.0000004 v -2.15625 c -0.341622,-1.9855 -1.166754,-4.216 -3.25,-3.7188 -1.859893,0.4439 -0.963423,3.329 -2.875,3.375 -1.7040124,0.041 -1.1715544,-5.69782 -2.5000004,-5.25002 -1.625796,0.548 -0.880393,2.90822 -2,3.62502 -0.779327,0.4989 -1.00982,-9.11672 -3.5,-9.87502 -0.122984,-0.038 -0.237265,-0.048 -0.34375,-0.031 z"
id="rect3106-1"
- d="m -17.715754,987.94743 c -1.597279,0.2543 -1.46135,6.3487 -3.28125,9.78122 -0.652424,1.2305 -1.528719,2.35895 -2.25,3.21885 l 0,1.0312 20.0000004,0 0,-2.15625 c -0.341622,-1.9855 -1.166754,-4.216 -3.25,-3.7188 -1.859893,0.4439 -0.963423,3.329 -2.875,3.375 -1.7040124,0.041 -1.1715544,-5.69782 -2.5000004,-5.25002 -1.625796,0.548 -0.880393,2.90822 -2,3.62502 -0.779327,0.4989 -1.00982,-9.11672 -3.5,-9.87502 -0.122984,-0.038 -0.237265,-0.048 -0.34375,-0.031 z"
- style="fill:#f0a0a0;fill-opacity:1;stroke:none" />
- <path
- inkscape:export-ydpi="216"
- inkscape:export-xdpi="216"
+ inkscape:connector-curvature="0"
inkscape:export-filename="D:\MITK\Modules\Segmentation\resources\Threshold_48x48.png"
- id="rect3106"
- d="m -17.715754,987.94733 c -1.597279,0.2543 -1.46135,6.3488 -3.28125,9.78132 -0.652424,1.2305 -1.528719,2.35885 -2.25,3.21875 l 0,1.0313 9.375,0 0,-4.12505 c -0.779327,0.4989 -1.00982,-9.11672 -3.5,-9.87502 -0.122984,-0.038 -0.237265,-0.048 -0.34375,-0.031 z"
- style="fill:#808080;fill-opacity:1;stroke:none"
- inkscape:connector-curvature="0" />
- <path
- inkscape:export-ydpi="216"
inkscape:export-xdpi="216"
- inkscape:export-filename="D:\MITK\Modules\Segmentation\resources\Threshold_48x48.png"
+ inkscape:export-ydpi="216" />
+ <path
inkscape:connector-curvature="0"
+ style="fill:#808080;fill-opacity:1;stroke:none"
+ d="m -17.715754,987.94733 c -1.597279,0.2543 -1.46135,6.3488 -3.28125,9.78132 -0.652424,1.2305 -1.528719,2.35885 -2.25,3.21875 v 1.0313 h 9.375 v -4.12505 c -0.779327,0.4989 -1.00982,-9.11672 -3.5,-9.87502 -0.122984,-0.038 -0.237265,-0.048 -0.34375,-0.031 z"
+ id="rect3106"
+ inkscape:export-filename="D:\MITK\Modules\Segmentation\resources\Threshold_48x48.png"
+ inkscape:export-xdpi="216"
+ inkscape:export-ydpi="216" />
+ <path
+ style="fill:#f0a0a0;fill-opacity:1;stroke:#00adff;stroke-width:1.0064;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m -14.059504,982.47769 v 18.99451"
id="path3881"
- d="m -14.059504,982.47769 0,18.99451"
- style="fill:#f0a0a0;fill-opacity:1;stroke:#00adff;stroke-width:1.00640261;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ inkscape:connector-curvature="0"
+ inkscape:export-filename="D:\MITK\Modules\Segmentation\resources\Threshold_48x48.png"
+ inkscape:export-xdpi="216"
+ inkscape:export-ydpi="216" />
</g>
</g>
<g
- inkscape:groupmode="layer"
- id="layer12"
+ sodipodi:insensitive="true"
inkscape:label="Two Thresholds"
- sodipodi:insensitive="true">
+ id="layer12"
+ inkscape:groupmode="layer">
<g
id="g4039">
<path
- inkscape:export-ydpi="216"
- inkscape:export-xdpi="216"
- inkscape:export-filename="D:\MITK\Modules\Segmentation\resources\TwoThresholds_48x48.png"
- id="path3954"
- d="m -6.7645731,1025.6384 2.8125,0 0,-2.1562 c -0.313812,-1.8239 -1.082283,-3.7544 -2.8125,-3.6876 l 0,5.8438 z"
+ inkscape:connector-curvature="0"
style="fill:#808080;fill-opacity:1;stroke:none"
- inkscape:connector-curvature="0" />
- <path
- inkscape:export-ydpi="216"
- inkscape:export-xdpi="216"
+ d="m -6.7645731,1025.6384 h 2.8125 v -2.1562 c -0.313812,-1.8239 -1.082283,-3.7544 -2.8125,-3.6876 z"
+ id="path3954"
inkscape:export-filename="D:\MITK\Modules\Segmentation\resources\TwoThresholds_48x48.png"
- id="path3952"
- d="m -18.202073,1011.6384 c -0.07469,-0.011 -0.150669,-0.042 -0.21875,-0.031 -1.597279,0.2542 -1.46135,6.3487 -3.28125,9.7812 -0.652424,1.2305 -1.528719,2.3588 -2.25,3.2188 l 0,1.0312 5.75,0 0,-14 z"
- style="fill:#808080;fill-opacity:1;stroke:none"
- inkscape:connector-curvature="0" />
- <path
- inkscape:export-ydpi="216"
inkscape:export-xdpi="216"
- inkscape:export-filename="D:\MITK\Modules\Segmentation\resources\TwoThresholds_48x48.png"
- id="rect3106-1-7"
- d="m -18.202073,1011.6384 0,14 11.4374999,0 0,-5.8438 c -0.15333,0.01 -0.267914,-0.072 -0.4375,-0.031 -1.859893,0.4439 -0.963423,3.329 -2.8749999,3.375 -1.704012,0.041 -1.171554,-5.6978 -2.5,-5.25 -1.625796,0.548 -0.880393,2.9082 -2,3.625 -0.779327,0.4989 -1.00982,-9.1167 -3.5,-9.875 -0.04436,-0.014 -0.08287,0.01 -0.125,0 z"
- style="fill:#f0a0a0;fill-opacity:1;stroke:none"
- inkscape:connector-curvature="0" />
+ inkscape:export-ydpi="216" />
<path
- inkscape:export-ydpi="216"
- inkscape:export-xdpi="216"
- inkscape:export-filename="D:\MITK\Modules\Segmentation\resources\TwoThresholds_48x48.png"
inkscape:connector-curvature="0"
- id="path3881-0"
- d="m -18.263221,1006.2664 0,18.8596"
- style="fill:#f0a0a0;fill-opacity:1;stroke:#00adff;stroke-width:1.00270426;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- inkscape:export-ydpi="216"
+ style="fill:#808080;fill-opacity:1;stroke:none"
+ d="m -18.202073,1011.6384 c -0.07469,-0.011 -0.150669,-0.042 -0.21875,-0.031 -1.597279,0.2542 -1.46135,6.3487 -3.28125,9.7812 -0.652424,1.2305 -1.528719,2.3588 -2.25,3.2188 v 1.0312 h 5.75 v -14 z"
+ id="path3952"
+ inkscape:export-filename="D:\MITK\Modules\Segmentation\resources\TwoThresholds_48x48.png"
inkscape:export-xdpi="216"
+ inkscape:export-ydpi="216" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#f0a0a0;fill-opacity:1;stroke:none"
+ d="m -18.202073,1011.6384 v 14 h 11.4374999 v -5.8438 c -0.15333,0.01 -0.267914,-0.072 -0.4375,-0.031 -1.859893,0.4439 -0.963423,3.329 -2.8749999,3.375 -1.704012,0.041 -1.171554,-5.6978 -2.5,-5.25 -1.625796,0.548 -0.880393,2.9082 -2,3.625 -0.779327,0.4989 -1.00982,-9.1167 -3.5,-9.875 -0.04436,-0.014 -0.08287,0.01 -0.125,0 z"
+ id="rect3106-1-7"
inkscape:export-filename="D:\MITK\Modules\Segmentation\resources\TwoThresholds_48x48.png"
+ inkscape:export-xdpi="216"
+ inkscape:export-ydpi="216" />
+ <path
+ style="fill:#f0a0a0;fill-opacity:1;stroke:#00adff;stroke-width:1.0027;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m -18.263221,1006.2664 v 18.8596"
+ id="path3881-0"
inkscape:connector-curvature="0"
+ inkscape:export-filename="D:\MITK\Modules\Segmentation\resources\TwoThresholds_48x48.png"
+ inkscape:export-xdpi="216"
+ inkscape:export-ydpi="216" />
+ <path
+ style="fill:#f0a0a0;fill-opacity:1;stroke:#00adff;stroke-width:1.0027;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m -6.8909251,1006.2664 v 18.8596"
id="path3881-0-4"
- d="m -6.8909251,1006.2664 0,18.8596"
- style="fill:#f0a0a0;fill-opacity:1;stroke:#00adff;stroke-width:1.00270426;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ inkscape:connector-curvature="0"
+ inkscape:export-filename="D:\MITK\Modules\Segmentation\resources\TwoThresholds_48x48.png"
+ inkscape:export-xdpi="216"
+ inkscape:export-ydpi="216" />
</g>
</g>
<g
- inkscape:groupmode="layer"
- id="layer8"
+ sodipodi:insensitive="true"
inkscape:label="Correction"
- sodipodi:insensitive="true">
+ id="layer8"
+ inkscape:groupmode="layer">
<g
- id="g4966"
- transform="translate(-46.657501,580.10279)">
+ transform="translate(-46.657501,580.10279)"
+ id="g4966">
<g
- inkscape:export-ydpi="211.62344"
- inkscape:export-xdpi="211.62344"
- inkscape:export-filename="C:\Users\Stefan\Desktop\correction48x48.png"
+ transform="translate(-165.54255,6.499221)"
id="g3802-4"
- transform="translate(-165.54255,6.499221)">
+ inkscape:export-filename="C:\Users\Stefan\Desktop\correction48x48.png"
+ inkscape:export-xdpi="211.62344"
+ inkscape:export-ydpi="211.62344">
<path
- style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m 271.75,420.48718 c 0,7.25 0,7.375 0,7.375"
+ inkscape:connector-curvature="0"
id="path3778-9"
- inkscape:connector-curvature="0" />
+ d="m 271.75,420.48718 c 0,7.25 0,7.375 0,7.375"
+ style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
- style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m 268.0625,424.11218 c 7.25,0 7.375,0 7.375,0"
+ inkscape:connector-curvature="0"
id="path3778-1-2"
- inkscape:connector-curvature="0" />
+ d="m 268.0625,424.11218 c 7.25,0 7.375,0 7.375,0"
+ style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
- inkscape:export-ydpi="211.62344"
- inkscape:export-xdpi="211.62344"
- inkscape:export-filename="C:\Users\Stefan\Desktop\correction48x48.png"
+ transform="translate(-154.65312,17.754109)"
id="g3802-5-6"
- transform="translate(-154.65312,17.754109)">
+ inkscape:export-filename="C:\Users\Stefan\Desktop\correction48x48.png"
+ inkscape:export-xdpi="211.62344"
+ inkscape:export-ydpi="211.62344">
<path
- style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m 268.0625,424.11218 c 7.25,0 7.375,0 7.375,0"
+ inkscape:connector-curvature="0"
id="path3778-1-1-5"
- inkscape:connector-curvature="0" />
+ d="m 268.0625,424.11218 c 7.25,0 7.375,0 7.375,0"
+ style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<path
- inkscape:export-ydpi="211.62344"
- inkscape:export-xdpi="211.62344"
- inkscape:export-filename="C:\Users\Stefan\Desktop\correction48x48.png"
- sodipodi:nodetypes="csc"
- inkscape:connector-curvature="0"
- id="path4387"
+ style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 103.16688,445.2403 c 0.2154,-5.8508 4.90234,-8.52203 9.19239,-9.61665 6.13803,-1.56614 7.35853,-4.59814 9.33381,-7.9196"
- style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ id="path4387"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="csc"
+ inkscape:export-filename="C:\Users\Stefan\Desktop\correction48x48.png"
+ inkscape:export-xdpi="211.62344"
+ inkscape:export-ydpi="211.62344" />
</g>
</g>
<g
- inkscape:groupmode="layer"
- id="layer9"
+ sodipodi:insensitive="true"
inkscape:label="Fill"
- sodipodi:insensitive="true">
+ id="layer9"
+ inkscape:groupmode="layer">
<g
- id="g5054"
- transform="translate(-114.9172,554.93226)">
+ transform="translate(-114.9172,554.93226)"
+ id="g5054">
<g
- transform="matrix(0.90666946,0,0,0.90666946,5.5562749,39.228766)"
- id="g5021">
+ id="g5021"
+ transform="matrix(0.90666946,0,0,0.90666946,5.5562749,39.228766)">
<g
id="g5009">
<g
- id="g5073"
- style="fill:#00adff;fill-opacity:1" />
+ style="fill:#00adff;fill-opacity:1"
+ id="g5073" />
</g>
</g>
<g
- id="g5133"
- transform="translate(73.539105,41.896077)"
- inkscape:export-filename="C:\Users\Stefan\Desktop\fill48x48.png"
+ inkscape:export-ydpi="208.82001"
inkscape:export-xdpi="208.82001"
- inkscape:export-ydpi="208.82001">
+ inkscape:export-filename="C:\Users\Stefan\Desktop\fill48x48.png"
+ transform="translate(73.539105,41.896077)"
+ id="g5133">
<g
- style="fill:#00adff;fill-opacity:1"
+ transform="matrix(0.90666946,0,0,0.90666946,5.556275,39.228766)"
id="g5099"
- transform="matrix(0.90666946,0,0,0.90666946,5.556275,39.228766)">
+ style="fill:#00adff;fill-opacity:1">
<path
- id="rect4974"
- transform="matrix(1.1029378,0,0,1.1029378,-16.3304,-12.246763)"
- d="m 69.84375,404.21875 c -0.08109,-0.0192 -0.156532,0.007 -0.21875,0.0312 -0.0087,0.003 -0.02292,-0.004 -0.03125,0 l -10.40625,5.40625 4.3125,8.3125 10.40625,-5.4375 c 0.0122,-0.006 0.02012,-0.0231 0.03125,-0.0312 0.482409,-0.35498 -0.02611,-2.3854 -1.1875,-4.625 -1.039651,-2.00485 -2.258961,-3.50261 -2.90625,-3.65625 z"
+ inkscape:connector-curvature="0"
style="fill:url(#linearGradient5141);fill-opacity:1;stroke:none"
- inkscape:connector-curvature="0" />
+ d="m 69.84375,404.21875 c -0.08109,-0.0192 -0.156532,0.007 -0.21875,0.0312 -0.0087,0.003 -0.02292,-0.004 -0.03125,0 l -10.40625,5.40625 4.3125,8.3125 10.40625,-5.4375 c 0.0122,-0.006 0.02012,-0.0231 0.03125,-0.0312 0.482409,-0.35498 -0.02611,-2.3854 -1.1875,-4.625 -1.039651,-2.00485 -2.258961,-3.50261 -2.90625,-3.65625 z"
+ transform="matrix(1.1029378,0,0,1.1029378,-16.3304,-12.246763)"
+ id="rect4974" />
</g>
- <path
- transform="matrix(0.41850495,0.80430288,-0.80430288,0.41850495,391.77103,207.77835)"
- d="m 61.3125,451.54968 c 0,0.6731 -2.301536,1.21875 -5.140625,1.21875 -2.839089,0 -5.140625,-0.54565 -5.140625,-1.21875 0,-0.67309 2.301536,-1.21875 5.140625,-1.21875 2.839089,0 5.140625,0.54566 5.140625,1.21875 z"
- sodipodi:ry="1.21875"
- sodipodi:rx="5.140625"
- sodipodi:cy="451.54968"
- sodipodi:cx="56.171875"
- id="path4977"
+ <ellipse
+ ry="1.21875"
+ rx="5.140625"
+ cy="451.54968"
+ cx="56.171875"
style="fill:url(#linearGradient5143);fill-opacity:1;stroke:none"
- sodipodi:type="arc" />
+ id="path4977"
+ transform="matrix(0.41850495,0.80430288,-0.80430288,0.41850495,391.77103,207.77835)" />
<path
- sodipodi:nodetypes="csc"
- inkscape:connector-curvature="0"
- id="path5019"
+ transform="matrix(0.90666946,0,0,0.90666946,5.556275,39.228766)"
+ style="fill:none;stroke:#007eb8;stroke-width:0.551469;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 54.578607,443.81261 c 0,0 -0.950095,-7.34364 -2.501182,-8.8849 -0.773066,-0.76816 -0.456375,3.23746 -0.456375,3.23746"
- style="fill:none;stroke:#007eb8;stroke-width:0.55146891;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- transform="matrix(0.90666946,0,0,0.90666946,5.556275,39.228766)" />
- <path
- sodipodi:type="arc"
- style="fill:#007eb8;fill-opacity:1;stroke:none"
+ id="path5019"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="csc" />
+ <circle
+ r="0.375"
+ cy="441.58093"
+ cx="55.96875"
+ transform="matrix(1.4583333,0,0,1.4583333,-26.527344,-202.17251)"
id="path5034"
- sodipodi:cx="55.96875"
- sodipodi:cy="441.58093"
- sodipodi:rx="0.375"
- sodipodi:ry="0.375"
- d="m 56.34375,441.58093 c 0,0.20711 -0.167893,0.375 -0.375,0.375 -0.207107,0 -0.375,-0.16789 -0.375,-0.375 0,-0.2071 0.167893,-0.375 0.375,-0.375 0.207107,0 0.375,0.1679 0.375,0.375 z"
- transform="matrix(1.4583333,0,0,1.4583333,-26.527344,-202.17251)" />
+ style="fill:#007eb8;fill-opacity:1;stroke:none" />
<path
- inkscape:connector-curvature="0"
- style="fill:#f0a0a0;fill-opacity:1;stroke:none"
+ id="path5029"
d="m 53.9375,443.34375 c -0.49647,0.31615 -1.337012,0.93474 -1.40625,1.40625 0.105873,0.69145 0.319341,1.7934 0.1875,3.09375 -4.847506,0.21233 -8.53125,1.23756 -8.53125,2.5 0,1.41565 4.632332,2.5625 10.34375,2.5625 5.711418,0 10.34375,-1.14685 10.34375,-2.5625 0,-1.41565 -4.632332,-2.5625 -10.34375,-2.5625 -0.159692,0 -0.310826,-0.002 -0.46875,0 -0.02057,-0.55989 -0.01656,-1.12761 0.15625,-1.6875 0.474392,-0.19225 0.209659,-1.67055 -0.28125,-2.75 z"
- id="path5029" />
+ style="fill:#f0a0a0;fill-opacity:1;stroke:none"
+ inkscape:connector-curvature="0" />
</g>
</g>
</g>
<g
- inkscape:groupmode="layer"
- id="layer10"
+ sodipodi:insensitive="true"
+ style="display:inline"
inkscape:label="Erase"
- sodipodi:insensitive="true">
+ id="layer10"
+ inkscape:groupmode="layer">
<g
- id="g3886"
- inkscape:export-filename="D:\MITK\Modules\Segmentation\resources\Erase_48x48.png"
+ inkscape:export-ydpi="188.23814"
inkscape:export-xdpi="188.23814"
- inkscape:export-ydpi="188.23814">
+ inkscape:export-filename="D:\MITK\Modules\Segmentation\resources\Erase_48x48.png"
+ id="g3886">
<g
- inkscape:export-ydpi="197"
- inkscape:export-xdpi="197"
- inkscape:export-filename="C:\Users\Stefan\Desktop\erase48x48.png"
+ id="g5269"
transform="translate(-14.499968,597.0898)"
- id="g5269">
+ inkscape:export-filename="C:\Users\Stefan\Desktop\erase48x48.png"
+ inkscape:export-xdpi="197"
+ inkscape:export-ydpi="197">
<g
- inkscape:export-ydpi="187.57001"
- inkscape:export-xdpi="187.57001"
- inkscape:export-filename="C:\Users\Stefan\Desktop\erase_48x48.png"
+ id="g3865"
transform="translate(-0.875,-0.125)"
- id="g3865">
- <path
- transform="matrix(1,0,0,1.035533,-0.03125,-18.309983)"
- d="m 65.59375,451.53406 c 0,1.7 -4.896886,3.07812 -10.9375,3.07812 -6.040614,0 -10.9375,-1.37812 -10.9375,-3.07812 0,-1.7 4.896886,-3.07813 10.9375,-3.07813 6.040614,0 10.9375,1.37813 10.9375,3.07813 z"
- sodipodi:ry="3.078125"
- sodipodi:rx="10.9375"
- sodipodi:cy="451.53406"
- sodipodi:cx="54.65625"
+ inkscape:export-filename="C:\Users\Stefan\Desktop\erase_48x48.png"
+ inkscape:export-xdpi="187.57001"
+ inkscape:export-ydpi="187.57001">
+ <ellipse
+ ry="3.078125"
+ rx="10.9375"
+ cy="451.53406"
+ cx="54.65625"
+ style="fill:#f0a0a0;fill-opacity:0.352632;stroke:#f0a0a0;stroke-width:1.14582;stroke-miterlimit:4;stroke-dasharray:1.14582, 1.14582;stroke-dashoffset:0;stroke-opacity:1"
id="path5267"
- style="fill:#f0a0a0;fill-opacity:0.35263157;stroke:#f0a0a0;stroke-width:1.1458205;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1.14582048, 1.14582048;stroke-dashoffset:0"
- sodipodi:type="arc" />
+ transform="matrix(1,0,0,1.035533,-0.03125,-18.309983)" />
<g
- inkscape:export-ydpi="198.05157"
- inkscape:export-xdpi="198.05157"
- inkscape:export-filename="C:\Users\Stefan\Desktop\wipe48x48.png"
+ id="g4941-0"
transform="translate(0.09375,-0.665147)"
- id="g4941-0">
+ inkscape:export-filename="C:\Users\Stefan\Desktop\wipe48x48.png"
+ inkscape:export-xdpi="198.05157"
+ inkscape:export-ydpi="198.05157">
<g
- id="g4904-3"
- transform="matrix(0.93314753,0.35949366,-0.35949366,0.93314753,166.03247,8.0201654)">
+ transform="rotate(21.069103,61.452343,450.42305)"
+ id="g4904-3">
<g
id="g4900-4">
<rect
- style="fill:#ffffff;fill-opacity:1;stroke:none"
- id="rect4841-1"
- width="9.1428576"
- height="2.8857133"
+ y="444.79077"
x="50.114285"
- y="444.79077" />
- <path
- sodipodi:type="arc"
- style="fill:#ffffff;fill-opacity:1;stroke:none"
+ height="2.8857133"
+ width="9.1428576"
+ id="rect4841-1"
+ style="fill:#ffffff;fill-opacity:1;stroke:none" />
+ <ellipse
+ ry="2.8142858"
+ rx="4.5536098"
+ cy="450.43362"
+ cx="54.696468"
+ transform="translate(-0.01075506,-2.8285714)"
id="path4843-5"
- sodipodi:cx="54.696468"
- sodipodi:cy="450.43362"
- sodipodi:rx="4.5536098"
- sodipodi:ry="2.8142858"
- d="m 59.250078,450.43362 c 0,1.55429 -2.03872,2.81429 -4.55361,2.81429 -2.514889,0 -4.553609,-1.26 -4.553609,-2.81429 0,-1.55428 2.03872,-2.81428 4.553609,-2.81428 2.51489,0 4.55361,1.26 4.55361,2.81428 z"
- transform="translate(-0.01075506,-2.8285714)" />
+ style="fill:#ffffff;fill-opacity:1;stroke:none" />
</g>
<rect
- y="435.77646"
- x="50.114285"
- height="9.0285702"
- width="9.1428576"
+ style="fill:#00adff;fill-opacity:1;stroke:none"
id="rect4841-2-6"
- style="fill:#00adff;fill-opacity:1;stroke:none" />
+ width="9.1428576"
+ height="9.0285702"
+ x="50.114285"
+ y="435.77646" />
</g>
</g>
</g>
</g>
<g
- transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,142.15552,554.76912)"
- id="g3802-1">
+ id="g3802-1"
+ transform="rotate(45,-598.58781,448.98145)">
<g
- transform="translate(-1.3258252,0.08838835)"
- id="g3882">
+ id="g3882"
+ transform="translate(-1.3258252,0.08838835)">
<path
- style="fill:none;stroke:#00adff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m 271.75,420.48718 c 0,7.25 0,7.375 0,7.375"
+ inkscape:connector-curvature="0"
id="path3778-7"
- inkscape:connector-curvature="0" />
+ d="m 271.75,420.48718 c 0,7.25 0,7.375 0,7.375"
+ style="fill:none;stroke:#00adff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
- style="fill:none;stroke:#00adff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m 268.0625,424.11218 c 7.25,0 7.375,0 7.375,0"
+ inkscape:connector-curvature="0"
id="path3778-1-4"
- inkscape:connector-curvature="0" />
+ d="m 268.0625,424.11218 c 7.25,0 7.375,0 7.375,0"
+ style="fill:none;stroke:#00adff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</g>
</g>
</g>
<g
- inkscape:groupmode="layer"
- id="layer11"
- inkscape:label="Live Wire"
sodipodi:insensitive="true"
- style="display:inline">
+ style="display:inline"
+ inkscape:label="Live Wire"
+ id="layer11"
+ inkscape:groupmode="layer">
<g
- inkscape:export-ydpi="197.75497"
- inkscape:export-xdpi="197.75497"
- inkscape:export-filename="C:\Users\Stefan\Desktop\livewire48x48.png"
+ transform="translate(-203.29425,588.83644)"
id="g4272"
- transform="translate(-203.29425,588.83644)">
+ inkscape:export-filename="C:\Users\Stefan\Desktop\livewire48x48.png"
+ inkscape:export-xdpi="197.75497"
+ inkscape:export-ydpi="197.75497">
<g
- id="g5169"
- transform="translate(-1.75,-28.625)">
+ transform="translate(-1.75,-28.625)"
+ id="g5169">
<g
- id="g5181"
- inkscape:export-filename="C:\Users\Stefan\Desktop\livewire48x48.png"
- inkscape:export-xdpi="197"
+ transform="translate(137.88582,42.426407)"
inkscape:export-ydpi="197"
- transform="translate(137.88582,42.426407)">
+ inkscape:export-xdpi="197"
+ inkscape:export-filename="C:\Users\Stefan\Desktop\livewire48x48.png"
+ id="g5181">
<g
- transform="translate(-138.30625,21.017396)"
- id="g3822-4"
- inkscape:export-filename="C:\Users\Stefan\Desktop\add24x24.png"
+ inkscape:export-ydpi="98.877487"
inkscape:export-xdpi="98.877487"
- inkscape:export-ydpi="98.877487">
+ inkscape:export-filename="C:\Users\Stefan\Desktop\add24x24.png"
+ id="g3822-4"
+ transform="translate(-138.30625,21.017396)">
<g
id="g3808-6">
<path
- sodipodi:nodetypes="aaaaa"
- inkscape:connector-curvature="0"
- id="path3004-0"
+ style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 265.125,415.73718 c 2.66059,2.30475 8.06002,1.41215 10.5,-1.125 1.70795,-1.77597 2.30703,-5.69994 0.5,-7.375 -2.77871,-2.57579 -8.82218,-1.28388 -11.25,1.625 -1.4694,1.76055 -1.48328,5.37353 0.25,6.875 z"
- style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- sodipodi:nodetypes="cc"
+ id="path3004-0"
inkscape:connector-curvature="0"
- id="path3774-7"
- d="m 260.625,425.73718 c 6.84332,-2.29563 3.78655,-5.82877 5.625,-8.75"
- style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ sodipodi:nodetypes="aaaaa" />
<path
- transform="matrix(1.7077889,0.59262847,-0.4791464,1.3807655,37.309511,-275.74206)"
- d="m 247,396.11218 c 0,0.89747 -0.72754,1.625 -1.625,1.625 -0.89746,0 -1.625,-0.72753 -1.625,-1.625 0,-0.89746 0.72754,-1.625 1.625,-1.625 0.89746,0 1.625,0.72754 1.625,1.625 z"
- sodipodi:ry="1.625"
- sodipodi:rx="1.625"
- sodipodi:cy="396.11218"
- sodipodi:cx="245.375"
- id="path3776-1"
+ style="fill:none;stroke:#00adff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 260.625,425.73718 c 6.84332,-2.29563 3.78655,-5.82877 5.625,-8.75"
+ id="path3774-7"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <circle
+ r="1.625"
+ cy="396.11218"
+ cx="245.375"
style="fill:#00adff;fill-opacity:1;stroke:none"
- sodipodi:type="arc" />
+ id="path3776-1"
+ transform="matrix(1.7077889,0.59262847,-0.4791464,1.3807655,37.309511,-275.74206)" />
</g>
</g>
<g
- transform="matrix(0.76110092,0.29748319,-0.29748319,0.76110092,121.22005,49.100458)"
- id="g3925">
+ id="g3925"
+ transform="matrix(0.76110092,0.29748319,-0.29748319,0.76110092,121.22005,49.100458)">
<g
- id="g5164"
- transform="matrix(0.83524644,0.54987579,-0.54987579,0.83524644,274.27242,-33.958985)">
+ transform="rotate(33.358492,193.80638,440.72158)"
+ id="g5164">
<path
- style="fill:#808080;fill-opacity:1;stroke:none"
- d="m 189.55,434.43718 0,3.175 6.575,0 c 2.22087,0.17555 2.35031,3.36996 0,3.875 l -6.625,0 0,3.125 7.2,0 c 6.34407,-0.31013 6.05234,-10.08218 0.0437,-10.21875 z"
- id="path3831"
+ sodipodi:nodetypes="ccccccccc"
inkscape:connector-curvature="0"
- sodipodi:nodetypes="ccccccccc" />
+ id="path3831"
+ d="m 189.55,434.43718 v 3.175 h 6.575 c 2.22087,0.17555 2.35031,3.36996 0,3.875 H 189.5 v 3.125 h 7.2 c 6.34407,-0.31013 6.05234,-10.08218 0.0437,-10.21875 z"
+ style="fill:#808080;fill-opacity:1;stroke:none" />
<rect
- y="434.35162"
- x="189.53886"
- height="3.2905884"
- width="2.9771986"
+ style="fill:#00b700;fill-opacity:1;stroke:none"
id="rect3833"
- style="fill:#00b700;fill-opacity:1;stroke:none" />
+ width="2.9771986"
+ height="3.2905884"
+ x="189.53886"
+ y="434.35162" />
<path
- sodipodi:nodetypes="ccccc"
- inkscape:connector-curvature="0"
- id="rect3833-1"
+ style="fill:#b70000;fill-opacity:1;stroke:none"
d="m 189.48789,441.47736 3.05365,0.003 -0.0448,3.13606 -2.98657,-0.0462 z"
- style="fill:#b70000;fill-opacity:1;stroke:none" />
+ id="rect3833-1"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
</g>
</g>
</g>
</g>
</g>
</g>
<g
- inkscape:groupmode="layer"
- id="layer13"
+ sodipodi:insensitive="true"
inkscape:label="Otsu"
- sodipodi:insensitive="true">
+ id="layer13"
+ inkscape:groupmode="layer">
<text
- xml:space="preserve"
- style="font-size:10.22853947px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#00adff;fill-opacity:1;stroke:none;font-family:Sans"
- x="-52.019081"
- y="997.81415"
- id="text3119"
- sodipodi:linespacing="125%"
- inkscape:export-filename="D:\MITK\Modules\Segmentation\resources\Otsu_48x48.png"
+ inkscape:export-ydpi="223.91353"
inkscape:export-xdpi="223.91353"
- inkscape:export-ydpi="223.91353"><tspan
- sodipodi:role="line"
- id="tspan3121"
- x="-52.019081"
+ inkscape:export-filename="D:\MITK\Modules\Segmentation\resources\Otsu_48x48.png"
+ id="text3119"
+ y="997.81415"
+ x="-52.019081"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#00adff;fill-opacity:1;stroke:none"
+ xml:space="preserve"><tspan
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.2285px;line-height:1.25;font-family:'Arial Unicode MS';-inkscape-font-specification:'Arial Unicode MS';fill:#00adff;fill-opacity:1"
y="997.81415"
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#00adff;fill-opacity:1;font-family:Arial Unicode MS;-inkscape-font-specification:Arial Unicode MS">大津</tspan></text>
+ x="-52.019081"
+ id="tspan3121"
+ sodipodi:role="line">大津</tspan></text>
</g>
<g
- inkscape:groupmode="layer"
- id="layer14"
+ sodipodi:insensitive="true"
inkscape:label="Watershed"
- sodipodi:insensitive="true">
+ id="layer14"
+ inkscape:groupmode="layer">
<g
- id="g4017"
- transform="matrix(0.38533047,0,0,0.38533047,38.138941,626.25865)"
- inkscape:export-filename="D:\MITK\Modules\Segmentation\Resources\Watershed_48x48.png"
+ inkscape:export-ydpi="203.09"
inkscape:export-xdpi="203.09"
- inkscape:export-ydpi="203.09">
+ inkscape:export-filename="D:\MITK\Modules\Segmentation\Resources\Watershed_48x48.png"
+ transform="matrix(0.38533047,0,0,0.38533047,38.138941,626.25865)"
+ id="g4017">
<path
- sodipodi:nodetypes="zacacaaacacaz"
- inkscape:connector-curvature="0"
- id="path3122"
+ style="fill:url(#linearGradient3916);fill-opacity:1;stroke:none"
d="m -194.48262,992.98217 c -0.8006,-2.98859 -3.52623,-5.88632 -6.53987,-6.58409 -3.22924,-0.74769 -9.39542,3.25717 -9.39542,3.25717 0,0 -4.13025,-4.36363 -6.81561,-4.29939 -2.49879,0.0598 -6.14354,4.29939 -6.14354,4.29939 0,0 -6.76602,-2.99231 -9.36781,-1.21853 -2.51556,1.715 -2.75686,5.86603 -2.21272,8.86155 0.57839,3.18413 2.74392,7.46913 5.51453,7.99043 3.43025,0.6453 8.82326,-6.94838 8.82326,-6.94838 0,0 3.71498,8.07278 6.89317,8.33808 3.41174,0.2846 8.2718,-7.64315 8.2718,-7.64315 0,0 7.99677,1.44045 10.47761,-1.04223 1.18636,-1.18726 1.2952,-2.02226 0.4946,-5.01085 z"
- style="fill:url(#linearGradient3916);fill-opacity:1;stroke:none" />
- <path
- sodipodi:nodetypes="aacacaacacaa"
+ id="path3122"
inkscape:connector-curvature="0"
- id="path3124"
- d="m -227.51492,998.47867 c 1.84881,-4.15823 6.93467,-7.49929 11.48389,-7.3825 3.28074,0.0842 7.99771,5.74194 7.99771,5.74194 0,0 4.83231,-6.16107 8.20277,-5.94701 3.08875,0.19616 6.7673,6.35714 6.7673,6.35714 0,0 5.92782,-4.73599 8.40785,-3.07603 5.62841,3.76726 6.46607,16.56069 0.82028,20.30189 -3.33916,2.2127 -10.86869,-5.1268 -10.86869,-5.1268 0,0 -6.4187,6.3483 -10.45854,6.1521 -3.71462,-0.1804 -9.02306,-6.5622 -9.02306,-6.5622 0,0 -7.81193,5.3261 -11.07375,3.2811 -3.93227,-2.4653 -4.14133,-9.4987 -2.25576,-13.73963 z"
- style="fill:url(#linearGradient3908);fill-opacity:1;stroke:none" />
+ sodipodi:nodetypes="zacacaaacacaz" />
<path
- sodipodi:nodetypes="cssc"
+ style="fill:url(#linearGradient3908);fill-opacity:1;stroke:none"
+ d="m -227.51492,998.47867 c 1.84881,-4.15823 6.93467,-7.49929 11.48389,-7.3825 3.28074,0.0842 7.99771,5.74194 7.99771,5.74194 0,0 4.83231,-6.16107 8.20277,-5.94701 3.08875,0.19616 6.7673,6.35714 6.7673,6.35714 0,0 5.92782,-4.73599 8.40785,-3.07603 5.62841,3.76726 6.46607,16.56069 0.82028,20.30189 -3.33916,2.2127 -10.86869,-5.1268 -10.86869,-5.1268 0,0 -6.4187,6.3483 -10.45854,6.1521 -3.71462,-0.1804 -9.02306,-6.5622 -9.02306,-6.5622 0,0 -7.81193,5.3261 -11.07375,3.2811 -3.93227,-2.4653 -4.14133,-9.4987 -2.25576,-13.73963 z"
+ id="path3124"
inkscape:connector-curvature="0"
- id="path3924"
- d="m -229.02045,1013.5894 c 0,0 -4.12217,6.5258 -2.21681,8.8672 0.95041,1.1679 3.61947,1.2089 4.51726,0 1.84087,-2.4786 -2.30045,-8.8672 -2.30045,-8.8672 z"
- style="fill:#00adff;fill-opacity:1;stroke:none" />
+ sodipodi:nodetypes="aacacaacacaa" />
<path
- sodipodi:nodetypes="cssc"
+ style="fill:#00adff;fill-opacity:1;stroke:none"
+ d="m -229.02045,1013.5894 c 0,0 -4.12217,6.5258 -2.21681,8.8672 0.95041,1.1679 3.61947,1.2089 4.51726,0 1.84087,-2.4786 -2.30045,-8.8672 -2.30045,-8.8672 z"
+ id="path3924"
inkscape:connector-curvature="0"
- id="path3924-3"
- d="m -214.56927,1015.6594 c 0,0 -4.12217,6.5259 -2.2168,8.8673 0.95041,1.1678 3.61947,1.2088 4.51725,0 1.84088,-2.4787 -2.30045,-8.8673 -2.30045,-8.8673 z"
- style="fill:#00adff;fill-opacity:1;stroke:none" />
+ sodipodi:nodetypes="cssc" />
<path
- sodipodi:nodetypes="cssc"
+ style="fill:#00adff;fill-opacity:1;stroke:none"
+ d="m -214.56927,1015.6594 c 0,0 -4.12217,6.5259 -2.2168,8.8673 0.95041,1.1678 3.61947,1.2088 4.51725,0 1.84088,-2.4787 -2.30045,-8.8673 -2.30045,-8.8673 z"
+ id="path3924-3"
inkscape:connector-curvature="0"
- id="path3924-6"
- d="m -194.71733,1017.5827 c 0,0 -4.12217,6.5258 -2.2168,8.8673 0.95041,1.1678 3.61947,1.2088 4.51726,0 1.84087,-2.4787 -2.30046,-8.8673 -2.30046,-8.8673 z"
- style="fill:#00adff;fill-opacity:1;stroke:none" />
+ sodipodi:nodetypes="cssc" />
<path
- sodipodi:nodetypes="cssc"
+ style="fill:#00adff;fill-opacity:1;stroke:none"
+ d="m -194.71733,1017.5827 c 0,0 -4.12217,6.5258 -2.2168,8.8673 0.95041,1.1678 3.61947,1.2088 4.51726,0 1.84087,-2.4787 -2.30046,-8.8673 -2.30046,-8.8673 z"
+ id="path3924-6"
inkscape:connector-curvature="0"
- id="path3924-8"
- d="m -197.50319,1030.3135 c 0,0 -4.12217,6.5258 -2.21681,8.8672 0.95041,1.1679 3.61947,1.2088 4.51726,0 1.84087,-2.4786 -2.30045,-8.8672 -2.30045,-8.8672 z"
- style="fill:#00adff;fill-opacity:1;stroke:none" />
+ sodipodi:nodetypes="cssc" />
<path
- sodipodi:nodetypes="cssc"
+ style="fill:#00adff;fill-opacity:1;stroke:none"
+ d="m -197.50319,1030.3135 c 0,0 -4.12217,6.5258 -2.21681,8.8672 0.95041,1.1679 3.61947,1.2088 4.51726,0 1.84087,-2.4786 -2.30045,-8.8672 -2.30045,-8.8672 z"
+ id="path3924-8"
inkscape:connector-curvature="0"
- id="path3924-82"
- d="m -187.09253,1026.2911 c 0,0 -4.12217,6.5258 -2.2168,8.8672 0.95041,1.1679 3.61947,1.2089 4.51726,0 1.84087,-2.4786 -2.30046,-8.8672 -2.30046,-8.8672 z"
- style="fill:#00adff;fill-opacity:1;stroke:none" />
+ sodipodi:nodetypes="cssc" />
<path
- sodipodi:nodetypes="cssc"
+ style="fill:#00adff;fill-opacity:1;stroke:none"
+ d="m -187.09253,1026.2911 c 0,0 -4.12217,6.5258 -2.2168,8.8672 0.95041,1.1679 3.61947,1.2089 4.51726,0 1.84087,-2.4786 -2.30046,-8.8672 -2.30046,-8.8672 z"
+ id="path3924-82"
inkscape:connector-curvature="0"
- id="path3924-7"
- d="m -204.55675,1021.3 c 0,0 -4.12218,6.5259 -2.21681,8.8673 0.95041,1.1678 3.61947,1.2088 4.51726,0 1.84087,-2.4787 -2.30045,-8.8673 -2.30045,-8.8673 z"
- style="fill:#00adff;fill-opacity:1;stroke:none" />
+ sodipodi:nodetypes="cssc" />
<path
- sodipodi:nodetypes="cssc"
+ style="fill:#00adff;fill-opacity:1;stroke:none"
+ d="m -204.55675,1021.3 c 0,0 -4.12218,6.5259 -2.21681,8.8673 0.95041,1.1678 3.61947,1.2088 4.51726,0 1.84087,-2.4787 -2.30045,-8.8673 -2.30045,-8.8673 z"
+ id="path3924-7"
inkscape:connector-curvature="0"
- id="path3924-7-7"
- d="m -212.4094,1030.3136 c 0,0 -4.12217,6.5258 -2.2168,8.8672 0.95041,1.1679 3.61947,1.2088 4.51726,0 1.84087,-2.4786 -2.30046,-8.8672 -2.30046,-8.8672 z"
- style="fill:#00adff;fill-opacity:1;stroke:none" />
+ sodipodi:nodetypes="cssc" />
<path
- sodipodi:nodetypes="cssc"
+ style="fill:#00adff;fill-opacity:1;stroke:none"
+ d="m -212.4094,1030.3136 c 0,0 -4.12217,6.5258 -2.2168,8.8672 0.95041,1.1679 3.61947,1.2088 4.51726,0 1.84087,-2.4786 -2.30046,-8.8672 -2.30046,-8.8672 z"
+ id="path3924-7-7"
inkscape:connector-curvature="0"
- id="path3924-1"
+ sodipodi:nodetypes="cssc" />
+ <path
+ style="fill:#00adff;fill-opacity:1;stroke:none"
d="m -222.17541,1026.6021 c 0,0 -4.12217,6.5258 -2.21681,8.8672 0.95041,1.1679 3.61947,1.2089 4.51726,0 1.84087,-2.4786 -2.30045,-8.8672 -2.30045,-8.8672 z"
- style="fill:#00adff;fill-opacity:1;stroke:none" />
+ id="path3924-1"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cssc" />
</g>
</g>
</svg>
diff --git a/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp b/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp
index 6bf2ba52ed..2808f95126 100644
--- a/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp
+++ b/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp
@@ -1,185 +1,185 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkBooleanOperation.h"
#include <itkAndImageFilter.h>
#include <itkNotImageFilter.h>
#include <itkOrImageFilter.h>
#include <mitkExceptionMacro.h>
#include <mitkImageCast.h>
#include <mitkImageTimeSelector.h>
typedef itk::Image<mitk::Label::PixelType, 3> ImageType;
static mitk::Image::Pointer Get3DSegmentation(mitk::Image::Pointer segmentation, mitk::TimePointType time)
{
if (segmentation->GetDimension() != 4)
return segmentation;
auto imageTimeSelector = mitk::ImageTimeSelector::New();
imageTimeSelector->SetInput(segmentation);
imageTimeSelector->SetTimeNr(segmentation->GetTimeGeometry()->TimePointToTimeStep(time));
imageTimeSelector->UpdateLargestPossibleRegion();
return imageTimeSelector->GetOutput();
}
static ImageType::Pointer CastTo3DItkImage(mitk::Image::Pointer segmentation, mitk::TimePointType time)
{
ImageType::Pointer result;
mitk::CastToItkImage(Get3DSegmentation(segmentation, time), result);
return result;
}
mitk::BooleanOperation::BooleanOperation(Type type,
mitk::Image::Pointer segmentationA,
mitk::Image::Pointer segmentationB,
TimePointType time)
: m_Type(type), m_SegmentationA(segmentationA), m_SegmentationB(segmentationB), m_TimePoint(time)
{
this->ValidateSegmentations();
}
mitk::BooleanOperation::~BooleanOperation()
{
}
mitk::LabelSetImage::Pointer mitk::BooleanOperation::GetResult() const
{
switch (m_Type)
{
case Difference:
return this->GetDifference();
case Intersection:
return this->GetIntersection();
case Union:
return this->GetUnion();
default:
mitkThrow() << "Unknown boolean operation type '" << m_Type << "'!";
}
}
mitk::LabelSetImage::Pointer mitk::BooleanOperation::GetDifference() const
{
auto input1 = CastTo3DItkImage(m_SegmentationA, m_TimePoint);
auto input2 = CastTo3DItkImage(m_SegmentationB, m_TimePoint);
auto notFilter = itk::NotImageFilter<ImageType, ImageType>::New();
notFilter->SetInput(input2);
auto andFilter = itk::AndImageFilter<ImageType, ImageType>::New();
andFilter->SetInput1(input1);
andFilter->SetInput2(notFilter->GetOutput());
andFilter->UpdateLargestPossibleRegion();
auto tempResult = Image::New();
CastToMitkImage<ImageType>(andFilter->GetOutput(), tempResult);
tempResult->DisconnectPipeline();
auto result = mitk::LabelSetImage::New();
result->InitializeByLabeledImage(tempResult);
return result;
}
mitk::LabelSetImage::Pointer mitk::BooleanOperation::GetIntersection() const
{
auto input1 = CastTo3DItkImage(m_SegmentationA, m_TimePoint);
auto input2 = CastTo3DItkImage(m_SegmentationB, m_TimePoint);
auto andFilter = itk::AndImageFilter<ImageType, ImageType>::New();
andFilter->SetInput1(input1);
andFilter->SetInput2(input2);
andFilter->UpdateLargestPossibleRegion();
auto tempResult = Image::New();
CastToMitkImage<ImageType>(andFilter->GetOutput(), tempResult);
tempResult->DisconnectPipeline();
auto result = mitk::LabelSetImage::New();
result->InitializeByLabeledImage(tempResult);
return result;
}
mitk::LabelSetImage::Pointer mitk::BooleanOperation::GetUnion() const
{
auto input1 = CastTo3DItkImage(m_SegmentationA, m_TimePoint);
auto input2 = CastTo3DItkImage(m_SegmentationB, m_TimePoint);
auto orFilter = itk::OrImageFilter<ImageType, ImageType>::New();
orFilter->SetInput1(input1);
orFilter->SetInput2(input2);
orFilter->UpdateLargestPossibleRegion();
auto tempResult = Image::New();
CastToMitkImage<ImageType>(orFilter->GetOutput(), tempResult);
tempResult->DisconnectPipeline();
auto result = mitk::LabelSetImage::New();
result->InitializeByLabeledImage(tempResult);
return result;
}
void mitk::BooleanOperation::ValidateSegmentation(mitk::Image::Pointer segmentation) const
{
if (segmentation.IsNull())
mitkThrow() << "Segmentation is nullptr!";
if (segmentation->GetImageDescriptor()->GetNumberOfChannels() != 1)
mitkThrow() << "Segmentation has more than one channel!";
auto pixelType = segmentation->GetImageDescriptor()->GetChannelDescriptor().GetPixelType();
- if (pixelType.GetPixelType() != itk::ImageIOBase::SCALAR ||
- (pixelType.GetComponentType() != itk::ImageIOBase::UCHAR &&
- pixelType.GetComponentType() != itk::ImageIOBase::USHORT))
+ if (pixelType.GetPixelType() != itk::IOPixelEnum::SCALAR ||
+ (pixelType.GetComponentType() != itk::IOComponentEnum::UCHAR &&
+ pixelType.GetComponentType() != itk::IOComponentEnum::USHORT))
mitkThrow() << "Segmentation is neither of type 'unsigned char' nor type 'unsigned short'!";
auto dimension = segmentation->GetDimension();
if (dimension > 4)
mitkThrow() << "Segmentation has more than four dimensions!";
if (dimension < 3)
mitkThrow() << "Segmentation has less than three dimensions!";
if (!segmentation->GetTimeGeometry()->IsValidTimePoint(m_TimePoint))
mitkThrow() << "Segmentation is not defined for specified time point. Time point: " << m_TimePoint;
}
void mitk::BooleanOperation::ValidateSegmentations() const
{
this->ValidateSegmentation(m_SegmentationA);
this->ValidateSegmentation(m_SegmentationB);
if (m_SegmentationA->GetDimension() != m_SegmentationB->GetDimension())
mitkThrow() << "Segmentations have different dimensions!";
const auto geometryA = m_SegmentationA->GetTimeGeometry()->GetGeometryForTimePoint(m_TimePoint);
const auto geometryB = m_SegmentationB->GetTimeGeometry()->GetGeometryForTimePoint(m_TimePoint);
if (!mitk::Equal(*(geometryA.GetPointer()), *(geometryB.GetPointer()),eps,false))
{
mitkThrow() << "Segmentations have different geometries and cannot be used for boolean operations!";
}
}
diff --git a/Modules/Segmentation/files.cmake b/Modules/Segmentation/files.cmake
index 02b759d7b4..b1f29f073e 100644
--- a/Modules/Segmentation/files.cmake
+++ b/Modules/Segmentation/files.cmake
@@ -1,117 +1,119 @@
set(CPP_FILES
Algorithms/mitkCalculateSegmentationVolume.cpp
Algorithms/mitkContourModelSetToImageFilter.cpp
Algorithms/mitkContourSetToPointSetFilter.cpp
Algorithms/mitkContourUtils.cpp
Algorithms/mitkCorrectorAlgorithm.cpp
Algorithms/mitkDiffImageApplier.cpp
Algorithms/mitkDiffSliceOperation.cpp
Algorithms/mitkDiffSliceOperationApplier.cpp
Algorithms/mitkFeatureBasedEdgeDetectionFilter.cpp
Algorithms/mitkImageLiveWireContourModelFilter.cpp
Algorithms/mitkImageToContourFilter.cpp
#Algorithms/mitkImageToContourModelFilter.cpp
Algorithms/mitkImageToLiveWireContourFilter.cpp
Algorithms/mitkManualSegmentationToSurfaceFilter.cpp
Algorithms/mitkOtsuSegmentationFilter.cpp
Algorithms/mitkSegmentationObjectFactory.cpp
Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp
Algorithms/mitkShowSegmentationAsSmoothedSurface.cpp
Algorithms/mitkShowSegmentationAsSurface.cpp
Algorithms/mitkVtkImageOverwrite.cpp
Controllers/mitkSegmentationInterpolationController.cpp
Controllers/mitkToolManager.cpp
Controllers/mitkSegmentationModuleActivator.cpp
Controllers/mitkToolManagerProvider.cpp
DataManagement/mitkContour.cpp
DataManagement/mitkContourSet.cpp
DataManagement/mitkExtrudedContour.cpp
Interactions/mitkAdaptiveRegionGrowingTool.cpp
Interactions/mitkAddContourTool.cpp
Interactions/mitkAutoCropTool.cpp
Interactions/mitkAutoSegmentationTool.cpp
Interactions/mitkAutoSegmentationWithPreviewTool.cpp
Interactions/mitkAutoMLSegmentationWithPreviewTool.cpp
Interactions/mitkBinaryThresholdBaseTool.cpp
Interactions/mitkBinaryThresholdTool.cpp
Interactions/mitkBinaryThresholdULTool.cpp
Interactions/mitkCalculateGrayValueStatisticsTool.cpp
Interactions/mitkCalculateVolumetryTool.cpp
Interactions/mitkContourModelInteractor.cpp
Interactions/mitkContourModelLiveWireInteractor.cpp
Interactions/mitkLiveWireTool2D.cpp
Interactions/mitkContourTool.cpp
Interactions/mitkCreateSurfaceTool.cpp
Interactions/mitkDrawPaintbrushTool.cpp
Interactions/mitkErasePaintbrushTool.cpp
Interactions/mitkEraseRegionTool.cpp
Interactions/mitkFastMarchingBaseTool.cpp
Interactions/mitkFastMarchingTool.cpp
Interactions/mitkFastMarchingTool3D.cpp
Interactions/mitkFeedbackContourTool.cpp
Interactions/mitkFillRegionTool.cpp
Interactions/mitkOtsuTool3D.cpp
Interactions/mitkPaintbrushTool.cpp
Interactions/mitkPixelManipulationTool.cpp
Interactions/mitkRegionGrowingTool.cpp
Interactions/mitkSegmentationsProcessingTool.cpp
Interactions/mitkSetRegionTool.cpp
Interactions/mitkSegTool2D.cpp
Interactions/mitkSubtractContourTool.cpp
Interactions/mitkTool.cpp
Interactions/mitkToolCommand.cpp
Interactions/mitkWatershedTool.cpp
Interactions/mitkPickingTool.cpp
Interactions/mitknnUnetTool.cpp
Interactions/mitkSegmentationInteractor.cpp #SO
Interactions/mitkProcessExecutor.cpp
Rendering/mitkContourMapper2D.cpp
Rendering/mitkContourSetMapper2D.cpp
Rendering/mitkContourSetVtkMapper3D.cpp
Rendering/mitkContourVtkMapper3D.cpp
SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp
SegmentationUtilities/MorphologicalOperations/mitkMorphologicalOperations.cpp
#Added from ML
Controllers/mitkSliceBasedInterpolationController.cpp
Algorithms/mitkSurfaceStampImageFilter.cpp
)
set(RESOURCE_FILES
Add_48x48.png
Add_Cursor_32x32.png
+ AI_48x48.png
+ AI_Cursor_32x32.png
Erase_48x48.png
Erase_Cursor_32x32.png
FastMarching_48x48.png
FastMarching_Cursor_32x32.png
Fill_48x48.png
Fill_Cursor_32x32.png
LiveWire_48x48.png
LiveWire_Cursor_32x32.png
Otsu_48x48.png
Paint_48x48.png
Paint_Cursor_32x32.png
Pick_48x48.png
RegionGrowing_48x48.png
RegionGrowing_Cursor_32x32.png
Subtract_48x48.png
Subtract_Cursor_32x32.png
Threshold_48x48.png
TwoThresholds_48x48.png
Watershed_48x48.png
Watershed_Cursor_32x32.png
Wipe_48x48.png
Wipe_Cursor_32x32.png
Interactions/dummy.xml
Interactions/LiveWireTool.xml
Interactions/FastMarchingTool.xml
Interactions/PickingTool.xml
Interactions/PressMoveRelease.xml
Interactions/PressMoveReleaseAndPointSetting.xml
Interactions/PressMoveReleaseWithCTRLInversion.xml
Interactions/PressMoveReleaseWithCTRLInversionAllMouseMoves.xml
Interactions/SegmentationToolsConfig.xml
Interactions/ContourModelModificationConfig.xml
Interactions/ContourModelModificationInteractor.xml
)
diff --git a/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.cpp b/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.cpp
index 22a29d589a..a7c5bf161d 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.cpp
@@ -1,1339 +1,1338 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "QmitkLabelSetWidget.h"
// mitk
#include <mitkAutoCropImageFilter.h>
#include <mitkCoreObjectFactory.h>
#include <mitkIOUtil.h>
#include <mitkLabelSetImage.h>
#include <mitkLabelSetImageToSurfaceThreadedFilter.h>
#include <mitkRenderingManager.h>
#include <mitkShowSegmentationAsSurface.h>
#include <mitkSliceBasedInterpolationController.h>
#include <mitkStatusBar.h>
#include <mitkSurfaceBasedInterpolationController.h>
#include <mitkToolManagerProvider.h>
// Qmitk
#include <QmitkDataStorageComboBox.h>
#include <QmitkNewSegmentationDialog.h>
#include <QmitkStyleManager.h>
#include <QmitkSearchLabelDialog.h>
// Qt
#include <QColorDialog>
#include <QCompleter>
#include <QDateTime>
#include <QFileDialog>
#include <QMenu>
#include <QMessageBox>
#include <QPushButton>
#include <QStringListModel>
#include <QWidgetAction>
// itk
#include <itksys/SystemTools.hxx>
// todo:
// berry
//#include <berryIPreferencesService.h>
QmitkLabelSetWidget::QmitkLabelSetWidget(QWidget *parent)
: QWidget(parent), m_DataStorage(nullptr), m_Completer(nullptr), m_ToolManager(nullptr)
{
m_Controls.setupUi(this);
m_ColorSequenceRainbow.GoToBegin();
- m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(mitk::ToolManagerProvider::MULTILABEL_SEGMENTATION);
+ m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager();
m_Controls.m_LabelSearchBox->setAlwaysShowClearIcon(true);
m_Controls.m_LabelSearchBox->setShowSearchIcon(true);
QStringList completionList;
completionList << "";
m_Completer = new QCompleter(completionList, this);
m_Completer->setCaseSensitivity(Qt::CaseInsensitive);
m_Controls.m_LabelSearchBox->setCompleter(m_Completer);
connect(m_Controls.m_LabelSearchBox, SIGNAL(returnPressed()), this, SLOT(OnSearchLabel()));
// connect( m_Controls.m_LabelSetTableWidget, SIGNAL(labelListModified(const QStringList&)), this, SLOT(
// OnLabelListModified(const QStringList&)) );
// connect( m_Controls.m_LabelSetTableWidget, SIGNAL(mergeLabel(int)), this, SLOT( OnMergeLabel(int)) );
QStringListModel *completeModel = static_cast<QStringListModel *>(m_Completer->model());
completeModel->setStringList(GetLabelStringList());
m_Controls.m_LabelSearchBox->setEnabled(false);
m_Controls.m_lblCaption->setText("");
InitializeTableWidget();
}
QmitkLabelSetWidget::~QmitkLabelSetWidget() {}
void QmitkLabelSetWidget::OnTableViewContextMenuRequested(const QPoint & /*pos*/)
{
int pixelValue = GetPixelValueOfSelectedItem();
if (-1 == pixelValue)
return;
QMenu *menu = new QMenu(m_Controls.m_LabelSetTableWidget);
if (m_Controls.m_LabelSetTableWidget->selectedItems().size() > 1)
{
QAction *mergeAction = new QAction(QIcon(":/Qmitk/MergeLabels.png"), "Merge selection on current label", this);
mergeAction->setEnabled(true);
QObject::connect(mergeAction, SIGNAL(triggered(bool)), this, SLOT(OnMergeLabels(bool)));
menu->addAction(mergeAction);
QAction *removeLabelsAction = new QAction(QIcon(":/Qmitk/RemoveLabel.png"), "Remove selected labels", this);
removeLabelsAction->setEnabled(true);
QObject::connect(removeLabelsAction, SIGNAL(triggered(bool)), this, SLOT(OnRemoveLabels(bool)));
menu->addAction(removeLabelsAction);
QAction *eraseLabelsAction = new QAction(QIcon(":/Qmitk/EraseLabel.png"), "Erase selected labels", this);
eraseLabelsAction->setEnabled(true);
QObject::connect(eraseLabelsAction, SIGNAL(triggered(bool)), this, SLOT(OnEraseLabels(bool)));
menu->addAction(eraseLabelsAction);
QAction *combineAndCreateSurfaceAction =
new QAction(QIcon(":/Qmitk/CreateSurface.png"), "Combine and create a surface", this);
combineAndCreateSurfaceAction->setEnabled(true);
QObject::connect(
combineAndCreateSurfaceAction, SIGNAL(triggered(bool)), this, SLOT(OnCombineAndCreateSurface(bool)));
// menu->addAction(combineAndCreateSurfaceAction); Not implemented
QAction *createMasksAction =
new QAction(QIcon(":/Qmitk/CreateMask.png"), "Create a mask for each selected label", this);
createMasksAction->setEnabled(true);
QObject::connect(createMasksAction, SIGNAL(triggered(bool)), this, SLOT(OnCreateMasks(bool)));
// menu->addAction(createMasksAction); Not implemented
QAction *combineAndCreateMaskAction =
new QAction(QIcon(":/Qmitk/CreateMask.png"), "Combine and create a mask", this);
combineAndCreateMaskAction->setEnabled(true);
QObject::connect(combineAndCreateMaskAction, SIGNAL(triggered(bool)), this, SLOT(OnCombineAndCreateMask(bool)));
// menu->addAction(combineAndCreateMaskAction); Not implemented
}
else
{
QAction *renameAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "Rename...", this);
renameAction->setEnabled(true);
QObject::connect(renameAction, SIGNAL(triggered(bool)), this, SLOT(OnRenameLabel(bool)));
menu->addAction(renameAction);
QAction *removeAction = new QAction(QIcon(":/Qmitk/RemoveLabel.png"), "Remove...", this);
removeAction->setEnabled(true);
QObject::connect(removeAction, SIGNAL(triggered(bool)), this, SLOT(OnRemoveLabel(bool)));
menu->addAction(removeAction);
QAction *eraseAction = new QAction(QIcon(":/Qmitk/EraseLabel.png"), "Erase...", this);
eraseAction->setEnabled(true);
QObject::connect(eraseAction, SIGNAL(triggered(bool)), this, SLOT(OnEraseLabel(bool)));
menu->addAction(eraseAction);
QAction *mergeAction = new QAction(QIcon(":/Qmitk/MergeLabels.png"), "Merge...", this);
mergeAction->setEnabled(true);
QObject::connect(mergeAction, SIGNAL(triggered(bool)), this, SLOT(OnMergeLabel(bool)));
menu->addAction(mergeAction);
QAction *randomColorAction = new QAction(QIcon(":/Qmitk/RandomColor.png"), "Random color", this);
randomColorAction->setEnabled(true);
QObject::connect(randomColorAction, SIGNAL(triggered(bool)), this, SLOT(OnRandomColor(bool)));
menu->addAction(randomColorAction);
QAction *viewOnlyAction = new QAction(QIcon(":/Qmitk/visible.png"), "View only", this);
viewOnlyAction->setEnabled(true);
QObject::connect(viewOnlyAction, SIGNAL(triggered(bool)), this, SLOT(OnSetOnlyActiveLabelVisible(bool)));
menu->addAction(viewOnlyAction);
QAction *viewAllAction = new QAction(QIcon(":/Qmitk/visible.png"), "View all", this);
viewAllAction->setEnabled(true);
QObject::connect(viewAllAction, SIGNAL(triggered(bool)), this, SLOT(OnSetAllLabelsVisible(bool)));
menu->addAction(viewAllAction);
QAction *hideAllAction = new QAction(QIcon(":/Qmitk/invisible.png"), "Hide all", this);
hideAllAction->setEnabled(true);
QObject::connect(hideAllAction, SIGNAL(triggered(bool)), this, SLOT(OnSetAllLabelsInvisible(bool)));
menu->addAction(hideAllAction);
QAction *lockAllAction = new QAction(QIcon(":/Qmitk/lock.png"), "Lock all", this);
lockAllAction->setEnabled(true);
QObject::connect(lockAllAction, SIGNAL(triggered(bool)), this, SLOT(OnLockAllLabels(bool)));
menu->addAction(lockAllAction);
QAction *unlockAllAction = new QAction(QIcon(":/Qmitk/unlock.png"), "Unlock all", this);
unlockAllAction->setEnabled(true);
QObject::connect(unlockAllAction, SIGNAL(triggered(bool)), this, SLOT(OnUnlockAllLabels(bool)));
menu->addAction(unlockAllAction);
QAction *createSurfaceAction = new QAction(QIcon(":/Qmitk/CreateSurface.png"), "Create surface", this);
createSurfaceAction->setEnabled(true);
createSurfaceAction->setMenu(new QMenu());
QAction *tmp1 = createSurfaceAction->menu()->addAction(QString("Detailed"));
QAction *tmp2 = createSurfaceAction->menu()->addAction(QString("Smoothed"));
QObject::connect(tmp1, SIGNAL(triggered(bool)), this, SLOT(OnCreateDetailedSurface(bool)));
QObject::connect(tmp2, SIGNAL(triggered(bool)), this, SLOT(OnCreateSmoothedSurface(bool)));
menu->addAction(createSurfaceAction);
QAction *createMaskAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Create mask", this);
createMaskAction->setEnabled(true);
QObject::connect(createMaskAction, SIGNAL(triggered(bool)), this, SLOT(OnCreateMask(bool)));
menu->addAction(createMaskAction);
QAction *createCroppedMaskAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Create cropped mask", this);
createCroppedMaskAction->setEnabled(true);
QObject::connect(createCroppedMaskAction, SIGNAL(triggered(bool)), this, SLOT(OnCreateCroppedMask(bool)));
// QAction* importAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "Import...", this );
// importAction->setEnabled(true);
// QObject::connect( importAction, SIGNAL( triggered(bool) ), this, SLOT( OnImportSegmentationSession(bool) ) );
// menu->addAction(importAction);
menu->addAction(createCroppedMaskAction);
QSlider *opacitySlider = new QSlider;
opacitySlider->setMinimum(0);
opacitySlider->setMaximum(100);
opacitySlider->setOrientation(Qt::Horizontal);
QObject::connect(opacitySlider, SIGNAL(valueChanged(int)), this, SLOT(OnOpacityChanged(int)));
QLabel *_OpacityLabel = new QLabel("Opacity: ");
QVBoxLayout *_OpacityWidgetLayout = new QVBoxLayout;
_OpacityWidgetLayout->setContentsMargins(4, 4, 4, 4);
_OpacityWidgetLayout->addWidget(_OpacityLabel);
_OpacityWidgetLayout->addWidget(opacitySlider);
QWidget *_OpacityWidget = new QWidget;
_OpacityWidget->setLayout(_OpacityWidgetLayout);
QWidgetAction *OpacityAction = new QWidgetAction(this);
OpacityAction->setDefaultWidget(_OpacityWidget);
// QObject::connect( m_OpacityAction, SIGNAL( changed() ), this, SLOT( OpacityActionChanged() ) );
auto workingImage = this->GetWorkingImage();
auto activeLayer = workingImage->GetActiveLayer();
auto label = workingImage->GetLabel(pixelValue, activeLayer);
if (nullptr != label)
{
auto opacity = label->GetOpacity();
opacitySlider->setValue(static_cast<int>(opacity * 100));
}
menu->addAction(OpacityAction);
}
menu->popup(QCursor::pos());
}
void QmitkLabelSetWidget::OnUnlockAllLabels(bool /*value*/)
{
GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsLocked(false);
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkLabelSetWidget::OnLockAllLabels(bool /*value*/)
{
GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsLocked(true);
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkLabelSetWidget::OnSetAllLabelsVisible(bool /*value*/)
{
GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsVisible(true);
UpdateAllTableWidgetItems();
}
void QmitkLabelSetWidget::OnSetAllLabelsInvisible(bool /*value*/)
{
GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsVisible(false);
UpdateAllTableWidgetItems();
}
void QmitkLabelSetWidget::OnSetOnlyActiveLabelVisible(bool /*value*/)
{
mitk::LabelSetImage *workingImage = GetWorkingImage();
int pixelValue = GetPixelValueOfSelectedItem();
workingImage->GetActiveLabelSet()->SetAllLabelsVisible(false);
workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->SetVisible(true);
workingImage->GetActiveLabelSet()->UpdateLookupTable(pixelValue);
this->WaitCursorOn();
const mitk::Point3D &pos =
workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetCenterOfMassCoordinates();
this->WaitCursorOff();
if (pos.GetVnlVector().max_value() > 0.0)
{
emit goToLabel(pos);
}
UpdateAllTableWidgetItems();
}
void QmitkLabelSetWidget::OnMergeLabel(bool /*value*/)
{
QmitkSearchLabelDialog dialog(this);
dialog.setWindowTitle("Select a second label..");
dialog.SetLabelSuggestionList(GetLabelStringList());
int dialogReturnValue = dialog.exec();
if (dialogReturnValue == QDialog::Rejected)
return;
int sourcePixelValue = -1;
for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); i++)
{
if (dialog.GetLabelSetWidgetTableCompleteWord() == QString(m_Controls.m_LabelSetTableWidget->item(i, 0)->text()))
sourcePixelValue = m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt();
}
if (sourcePixelValue == -1)
{
MITK_INFO << "unknown label";
return;
}
int pixelValue = GetPixelValueOfSelectedItem();
GetWorkingImage()->MergeLabel(pixelValue, sourcePixelValue, GetWorkingImage()->GetActiveLayer());
UpdateAllTableWidgetItems();
}
void QmitkLabelSetWidget::OnEraseLabel(bool /*value*/)
{
int pixelValue = GetPixelValueOfSelectedItem();
QString question = "Do you really want to erase the contents of label \"";
question.append(
QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetName()));
question.append("\"?");
QMessageBox::StandardButton answerButton =
QMessageBox::question(this, "Erase label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
if (answerButton == QMessageBox::Yes)
{
this->WaitCursorOn();
GetWorkingImage()->EraseLabel(pixelValue);
this->WaitCursorOff();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkLabelSetWidget::OnRemoveLabel(bool /*value*/)
{
int pixelValue = GetPixelValueOfSelectedItem();
QString question = "Do you really want to remove label \"";
question.append(
QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetName()));
question.append("\"?");
QMessageBox::StandardButton answerButton =
QMessageBox::question(this, "Remove label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
if (answerButton == QMessageBox::Yes)
{
this->WaitCursorOn();
GetWorkingImage()->GetActiveLabelSet()->RemoveLabel(pixelValue);
GetWorkingImage()->EraseLabel(pixelValue);
this->WaitCursorOff();
}
ResetAllTableWidgetItems();
}
void QmitkLabelSetWidget::OnRenameLabel(bool /*value*/)
{
int pixelValue = GetPixelValueOfSelectedItem();
QmitkNewSegmentationDialog dialog(this);
dialog.setWindowTitle("Rename Label");
dialog.SetSuggestionList(m_OrganColors);
dialog.SetColor(GetWorkingImage()->GetActiveLabelSet()->GetLabel(pixelValue)->GetColor());
dialog.SetSegmentationName(
QString::fromStdString(GetWorkingImage()->GetActiveLabelSet()->GetLabel(pixelValue)->GetName()));
if (dialog.exec() == QDialog::Rejected)
{
return;
}
QString segmentationName = dialog.GetSegmentationName();
if (segmentationName.isEmpty())
{
segmentationName = "Unnamed";
}
GetWorkingImage()->GetActiveLabelSet()->RenameLabel(pixelValue, segmentationName.toStdString(), dialog.GetColor());
GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue);
UpdateAllTableWidgetItems();
}
void QmitkLabelSetWidget::OnCombineAndCreateMask(bool /*value*/)
{
m_Controls.m_LabelSetTableWidget->selectedRanges();
// ...to do... //
}
void QmitkLabelSetWidget::OnCreateMasks(bool /*value*/)
{
m_Controls.m_LabelSetTableWidget->selectedRanges();
// ..to do.. //
}
void QmitkLabelSetWidget::OnCombineAndCreateSurface(bool /*value*/)
{
m_Controls.m_LabelSetTableWidget->selectedRanges();
// ..to do.. //
}
void QmitkLabelSetWidget::OnEraseLabels(bool /*value*/)
{
QString question = "Do you really want to erase the selected labels?";
QMessageBox::StandardButton answerButton = QMessageBox::question(
this, "Erase selected labels", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
if (answerButton == QMessageBox::Yes)
{
QList<QTableWidgetSelectionRange> ranges = m_Controls.m_LabelSetTableWidget->selectedRanges();
if (ranges.isEmpty())
return;
std::vector<mitk::Label::PixelType> VectorOfLablePixelValues;
foreach (QTableWidgetSelectionRange a, ranges)
for (int i = a.topRow(); i <= a.bottomRow(); i++)
VectorOfLablePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt());
this->WaitCursorOn();
GetWorkingImage()->EraseLabels(VectorOfLablePixelValues, GetWorkingImage()->GetActiveLayer());
this->WaitCursorOff();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkLabelSetWidget::OnRemoveLabels(bool /*value*/)
{
QString question = "Do you really want to remove selected labels?";
QMessageBox::StandardButton answerButton = QMessageBox::question(
this, "Remove selected labels", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
if (answerButton == QMessageBox::Yes)
{
QList<QTableWidgetSelectionRange> ranges = m_Controls.m_LabelSetTableWidget->selectedRanges();
if (ranges.isEmpty())
{
return;
}
std::vector<mitk::Label::PixelType> VectorOfLablePixelValues;
foreach (QTableWidgetSelectionRange a, ranges)
{
for (int i = a.topRow(); i <= a.bottomRow(); ++i)
{
VectorOfLablePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt());
}
}
this->WaitCursorOn();
GetWorkingImage()->RemoveLabels(VectorOfLablePixelValues, GetWorkingImage()->GetActiveLayer());
this->WaitCursorOff();
}
ResetAllTableWidgetItems();
}
void QmitkLabelSetWidget::OnMergeLabels(bool /*value*/)
{
int pixelValue = GetPixelValueOfSelectedItem();
QString question = "Do you really want to merge selected labels into \"";
question.append(
QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetName()));
question.append("\"?");
QMessageBox::StandardButton answerButton = QMessageBox::question(
this, "Merge selected label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
if (answerButton == QMessageBox::Yes)
{
QList<QTableWidgetSelectionRange> ranges = m_Controls.m_LabelSetTableWidget->selectedRanges();
if (ranges.isEmpty())
{
return;
}
std::vector<mitk::Label::PixelType> vectorOfSourcePixelValues;
foreach (QTableWidgetSelectionRange a, ranges)
{
for (int i = a.topRow(); i <= a.bottomRow(); ++i)
{
vectorOfSourcePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt());
}
}
this->WaitCursorOn();
GetWorkingImage()->MergeLabels(pixelValue, vectorOfSourcePixelValues, GetWorkingImage()->GetActiveLayer());
this->WaitCursorOff();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkLabelSetWidget::OnLockedButtonClicked()
{
int row = -1;
for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i)
{
if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i, LOCKED_COL))
{
row = i;
}
}
if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount())
{
int pixelValue = m_Controls.m_LabelSetTableWidget->item(row, 0)->data(Qt::UserRole).toInt();
GetWorkingImage()
->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())
->SetLocked(!GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetLocked());
}
}
void QmitkLabelSetWidget::OnVisibleButtonClicked()
{
int row = -1;
for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i)
{
if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i, VISIBLE_COL))
{
row = i;
break;
}
}
if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount())
{
QTableWidgetItem *item = m_Controls.m_LabelSetTableWidget->item(row, 0);
int pixelValue = item->data(Qt::UserRole).toInt();
GetWorkingImage()
->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())
->SetVisible(!GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetVisible());
GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue);
}
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkLabelSetWidget::OnColorButtonClicked()
{
int row = -1;
for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i)
{
if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i, COLOR_COL))
{
row = i;
}
}
if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount())
{
int pixelValue = m_Controls.m_LabelSetTableWidget->item(row, 0)->data(Qt::UserRole).toInt();
const mitk::Color &color = GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetColor();
QColor initial(color.GetRed() * 255, color.GetGreen() * 255, color.GetBlue() * 255);
QColor qcolor = QColorDialog::getColor(initial, nullptr, QString("Change color"));
if (!qcolor.isValid())
{
return;
}
QPushButton *button = static_cast<QPushButton *>(m_Controls.m_LabelSetTableWidget->cellWidget(row, COLOR_COL));
if (!button)
{
return;
}
button->setAutoFillBackground(true);
QString styleSheet = "background-color:rgb(";
styleSheet.append(QString::number(qcolor.red()));
styleSheet.append(",");
styleSheet.append(QString::number(qcolor.green()));
styleSheet.append(",");
styleSheet.append(QString::number(qcolor.blue()));
styleSheet.append("); border: 0;");
button->setStyleSheet(styleSheet);
mitk::Color newColor;
newColor.SetRed(qcolor.red() / 255.0);
newColor.SetGreen(qcolor.green() / 255.0);
newColor.SetBlue(qcolor.blue() / 255.0);
GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->SetColor(newColor);
GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue);
}
}
void QmitkLabelSetWidget::OnRandomColor(bool /*value*/)
{
int pixelValue = GetPixelValueOfSelectedItem();
GetWorkingImage()
->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())
->SetColor(m_ColorSequenceRainbow.GetNextColor());
GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue);
UpdateAllTableWidgetItems();
}
void QmitkLabelSetWidget::SetOrganColors(const QStringList &organColors)
{
m_OrganColors = organColors;
}
void QmitkLabelSetWidget::OnActiveLabelChanged(int pixelValue)
{
mitk::LabelSetImage *workingImage = GetWorkingImage();
assert(workingImage);
workingImage->GetActiveLabelSet()->SetActiveLabel(pixelValue);
// MITK_INFO << "Active Label set to << " << pixelValue;
mitk::SurfaceBasedInterpolationController *interpolator = mitk::SurfaceBasedInterpolationController::GetInstance();
if (interpolator)
{
interpolator->SetActiveLabel(pixelValue);
}
workingImage->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkLabelSetWidget::OnItemClicked(QTableWidgetItem *item)
{
if (!item)
return;
int pixelValue = item->data(Qt::UserRole).toInt();
QList<QTableWidgetSelectionRange> ranges = m_Controls.m_LabelSetTableWidget->selectedRanges();
if (!ranges.empty() && ranges.back().rowCount() == 1)
{
SelectLabelByPixelValue(pixelValue);
OnActiveLabelChanged(pixelValue);
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkLabelSetWidget::OnItemDoubleClicked(QTableWidgetItem *item)
{
if (!item)
return;
int pixelValue = item->data(Qt::UserRole).toInt();
// OnItemClicked(item); <<-- Double click first call OnItemClicked
WaitCursorOn();
mitk::LabelSetImage *workingImage = GetWorkingImage();
workingImage->UpdateCenterOfMass(pixelValue, workingImage->GetActiveLayer());
const mitk::Point3D &pos =
workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetCenterOfMassCoordinates();
WaitCursorOff();
if (pos.GetVnlVector().max_value() > 0.0)
{
emit goToLabel(pos);
}
workingImage->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkLabelSetWidget::SelectLabelByPixelValue(mitk::Label::PixelType pixelValue)
{
// MITK_INFO << "QmitkLabelSetWidget::SelectLabelByPixelValue " << pixelValue;
if (!GetWorkingImage()->ExistLabel(pixelValue))
return;
for (int row = 0; row < m_Controls.m_LabelSetTableWidget->rowCount(); row++)
{
if (m_Controls.m_LabelSetTableWidget->item(row, 0)->data(Qt::UserRole).toInt() == pixelValue)
{
m_Controls.m_LabelSetTableWidget->clearSelection();
m_Controls.m_LabelSetTableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
m_Controls.m_LabelSetTableWidget->selectRow(row);
m_Controls.m_LabelSetTableWidget->scrollToItem(m_Controls.m_LabelSetTableWidget->item(row, 0));
m_Controls.m_LabelSetTableWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
- // SelectTableWidgetItem(m_Controls.m_LabelSetTableWidget->item(i,0));
- // emit resetView();
- // GetWorkingImage()->Modified();
return;
}
}
}
void QmitkLabelSetWidget::InsertTableWidgetItem(mitk::Label *label)
{
const mitk::Color &color = label->GetColor();
QString styleSheet = "background-color:rgb(";
styleSheet.append(QString::number(color[0] * 255));
styleSheet.append(",");
styleSheet.append(QString::number(color[1] * 255));
styleSheet.append(",");
styleSheet.append(QString::number(color[2] * 255));
styleSheet.append("); border: 0;");
QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget;
int colWidth = (tableWidget->columnWidth(NAME_COL) < 180) ? 180 : tableWidget->columnWidth(NAME_COL) - 2;
QString text = fontMetrics().elidedText(label->GetName().c_str(), Qt::ElideMiddle, colWidth);
QTableWidgetItem *nameItem = new QTableWidgetItem(text);
nameItem->setTextAlignment(Qt::AlignCenter | Qt::AlignLeft);
// ---!---
// IMPORTANT: ADD PIXELVALUE TO TABLEWIDGETITEM.DATA
nameItem->setData(Qt::UserRole, QVariant(label->GetValue()));
// ---!---
QPushButton *pbColor = new QPushButton(tableWidget);
pbColor->setFixedSize(24, 24);
pbColor->setCheckable(false);
pbColor->setAutoFillBackground(true);
pbColor->setToolTip("Change label color");
pbColor->setStyleSheet(styleSheet);
connect(pbColor, SIGNAL(clicked()), this, SLOT(OnColorButtonClicked()));
QString transparentStyleSheet = QLatin1String("background-color: transparent; border: 0;");
QPushButton *pbLocked = new QPushButton(tableWidget);
pbLocked->setFixedSize(24, 24);
QIcon *iconLocked = new QIcon();
auto lockIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/lock.svg"));
auto unlockIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/unlock.svg"));
iconLocked->addPixmap(lockIcon.pixmap(64), QIcon::Normal, QIcon::Off);
iconLocked->addPixmap(unlockIcon.pixmap(64), QIcon::Normal, QIcon::On);
pbLocked->setIcon(*iconLocked);
pbLocked->setIconSize(QSize(24, 24));
pbLocked->setCheckable(true);
pbLocked->setToolTip("Lock/unlock label");
pbLocked->setChecked(!label->GetLocked());
pbLocked->setStyleSheet(transparentStyleSheet);
connect(pbLocked, SIGNAL(clicked()), this, SLOT(OnLockedButtonClicked()));
QPushButton *pbVisible = new QPushButton(tableWidget);
pbVisible->setFixedSize(24, 24);
pbVisible->setAutoRepeat(false);
QIcon *iconVisible = new QIcon();
auto visibleIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/visible.svg"));
auto invisibleIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/invisible.svg"));
iconVisible->addPixmap(visibleIcon.pixmap(64), QIcon::Normal, QIcon::Off);
iconVisible->addPixmap(invisibleIcon.pixmap(64), QIcon::Normal, QIcon::On);
pbVisible->setIcon(*iconVisible);
pbVisible->setIconSize(QSize(24, 24));
pbVisible->setCheckable(true);
pbVisible->setToolTip("Show/hide label");
pbVisible->setChecked(!label->GetVisible());
pbVisible->setStyleSheet(transparentStyleSheet);
connect(pbVisible, SIGNAL(clicked()), this, SLOT(OnVisibleButtonClicked()));
int row = tableWidget->rowCount();
tableWidget->insertRow(row);
tableWidget->setRowHeight(row, 24);
tableWidget->setItem(row, 0, nameItem);
tableWidget->setCellWidget(row, 1, pbLocked);
tableWidget->setCellWidget(row, 2, pbColor);
tableWidget->setCellWidget(row, 3, pbVisible);
tableWidget->selectRow(row);
// m_LabelSetImage->SetActiveLabel(label->GetPixelValue());
// m_ToolManager->WorkingDataModified.Send();
// emit activeLabelChanged(label->GetPixelValue());
if (row == 0)
{
tableWidget->hideRow(row); // hide exterior label
}
}
void QmitkLabelSetWidget::UpdateAllTableWidgetItems()
{
mitk::LabelSetImage *workingImage = GetWorkingImage();
if (!workingImage)
return;
// add all labels
QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget;
m_LabelStringList.clear();
for (int i = 0; i < tableWidget->rowCount(); ++i)
{
UpdateTableWidgetItem(tableWidget->item(i, 0));
m_LabelStringList.append(tableWidget->item(i, 0)->text());
}
OnLabelListModified(m_LabelStringList);
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkLabelSetWidget::UpdateTableWidgetItem(QTableWidgetItem *item)
{
mitk::LabelSetImage *workingImage = GetWorkingImage();
mitk::Label *label = workingImage->GetLabel(item->data(Qt::UserRole).toInt(), workingImage->GetActiveLayer());
const mitk::Color &color = label->GetColor();
QString styleSheet = "background-color:rgb(";
styleSheet.append(QString::number(color[0] * 255));
styleSheet.append(",");
styleSheet.append(QString::number(color[1] * 255));
styleSheet.append(",");
styleSheet.append(QString::number(color[2] * 255));
styleSheet.append("); border: 0;");
QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget;
int colWidth = (tableWidget->columnWidth(NAME_COL) < 180) ? 180 : tableWidget->columnWidth(NAME_COL) - 2;
QString text = fontMetrics().elidedText(label->GetName().c_str(), Qt::ElideMiddle, colWidth);
item->setText(text);
QPushButton *pbLocked = dynamic_cast<QPushButton *>(tableWidget->cellWidget(item->row(), 1));
pbLocked->setChecked(!label->GetLocked());
QPushButton *pbColor = dynamic_cast<QPushButton *>(tableWidget->cellWidget(item->row(), 2));
pbColor->setStyleSheet(styleSheet);
QPushButton *pbVisible = dynamic_cast<QPushButton *>(tableWidget->cellWidget(item->row(), 3));
pbVisible->setChecked(!label->GetVisible());
if (item->row() == 0)
{
tableWidget->hideRow(item->row()); // hide exterior label
}
}
void QmitkLabelSetWidget::ResetAllTableWidgetItems()
{
QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget;
// remove all rows
while (tableWidget->rowCount())
{
tableWidget->removeRow(0);
}
mitk::DataNode * workingNode = GetWorkingNode();
auto workingImage = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
if (nullptr == workingImage)
{
return;
}
// add all labels
m_LabelStringList.clear();
mitk::LabelSet::LabelContainerConstIteratorType it = workingImage->GetActiveLabelSet()->IteratorConstBegin();
mitk::LabelSet::LabelContainerConstIteratorType end = workingImage->GetActiveLabelSet()->IteratorConstEnd();
int pixelValue = -1;
while (it != end)
{
InsertTableWidgetItem(it->second);
if (workingImage->GetActiveLabel() == it->second) // get active
pixelValue = it->first;
m_LabelStringList.append(QString(it->second->GetName().c_str()));
it++;
}
SelectLabelByPixelValue(pixelValue);
OnLabelListModified(m_LabelStringList);
std::stringstream captionText;
captionText << "Number of labels: " << workingImage->GetNumberOfLabels(workingImage->GetActiveLayer()) - 1;
m_Controls.m_lblCaption->setText(captionText.str().c_str());
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
+
+ emit LabelSetWidgetReset();
}
int QmitkLabelSetWidget::GetPixelValueOfSelectedItem()
{
if (m_Controls.m_LabelSetTableWidget->currentItem())
{
return m_Controls.m_LabelSetTableWidget->currentItem()->data(Qt::UserRole).toInt();
}
return -1;
}
QStringList &QmitkLabelSetWidget::GetLabelStringList()
{
return m_LabelStringList;
}
void QmitkLabelSetWidget::InitializeTableWidget()
{
QTableWidget *tableWidged = m_Controls.m_LabelSetTableWidget;
tableWidged->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
tableWidged->setTabKeyNavigation(false);
tableWidged->setAlternatingRowColors(false);
tableWidged->setFocusPolicy(Qt::NoFocus);
tableWidged->setColumnCount(4);
tableWidged->resizeColumnToContents(NAME_COL);
tableWidged->setColumnWidth(LOCKED_COL, 25);
tableWidged->setColumnWidth(COLOR_COL, 25);
tableWidged->setColumnWidth(VISIBLE_COL, 25);
tableWidged->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
tableWidged->setContextMenuPolicy(Qt::CustomContextMenu);
tableWidged->horizontalHeader()->hide();
tableWidged->setSortingEnabled(false);
tableWidged->verticalHeader()->hide();
tableWidged->setEditTriggers(QAbstractItemView::NoEditTriggers);
tableWidged->setSelectionMode(QAbstractItemView::ExtendedSelection);
tableWidged->setSelectionBehavior(QAbstractItemView::SelectRows);
connect(tableWidged, SIGNAL(itemClicked(QTableWidgetItem *)), this, SLOT(OnItemClicked(QTableWidgetItem *)));
connect(
tableWidged, SIGNAL(itemDoubleClicked(QTableWidgetItem *)), this, SLOT(OnItemDoubleClicked(QTableWidgetItem *)));
connect(tableWidged,
SIGNAL(customContextMenuRequested(const QPoint &)),
this,
SLOT(OnTableViewContextMenuRequested(const QPoint &)));
// connect( m_Controls.m_LabelSetTableWidget, SIGNAL(activeLabelChanged(int)), this, SLOT(OnActiveLabelChanged(int))
// );
// connect( m_Controls.m_LabelSetTableWidget, SIGNAL(importSegmentation()), this, SLOT( OnImportSegmentation()) );
// connect( m_Controls.m_LabelSetTableWidget, SIGNAL(importLabeledImage()), this, SLOT( OnImportLabeledImage()) );
// connect( m_Controls.m_LabelSetTableWidget, SIGNAL(renameLabel(int, const mitk::Color&, const std::string&)), this,
// SLOT(OnRenameLabel(int, const mitk::Color&, const std::string&)) );
// connect( m_Controls.m_LabelSetTableWidget, SIGNAL(createSurface(int, bool)), this, SLOT(OnCreateSurface(int, bool))
// );
// connect( m_Controls.m_LabelSetTableWidget, SIGNAL(toggleOutline(bool)), this, SLOT(OnToggleOutline(bool)) );
// connect( m_Controls.m_LabelSetTableWidget, SIGNAL(goToLabel(const mitk::Point3D&)), this, SIGNAL(goToLabel(const
// mitk::Point3D&)) );
// connect( m_Controls.m_LabelSetTableWidget, SIGNAL(combineAndCreateSurface( const QList<QTableWidgetSelectionRange>&
// )),
// this, SLOT(OnCombineAndCreateSurface( const QList<QTableWidgetSelectionRange>&)) );
// connect( m_Controls.m_LabelSetTableWidget, SIGNAL(createMask(int)), this, SLOT(OnCreateMask(int)) );
// connect( m_Controls.m_LabelSetTableWidget, SIGNAL(createCroppedMask(int)), this, SLOT(OnCreateCroppedMask(int)) );
// connect( m_Controls.m_LabelSetTableWidget, SIGNAL(combineAndCreateMask( const QList<QTableWidgetSelectionRange>&
// )),
// this, SLOT(OnCombineAndCreateMask( const QList<QTableWidgetSelectionRange>&)) );
}
void QmitkLabelSetWidget::OnOpacityChanged(int value)
{
int pixelValue = GetPixelValueOfSelectedItem();
float opacity = static_cast<float>(value) / 100.0f;
GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->SetOpacity(opacity);
GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue);
}
void QmitkLabelSetWidget::setEnabled(bool enabled)
{
QWidget::setEnabled(enabled);
UpdateControls();
}
void QmitkLabelSetWidget::SetDataStorage(mitk::DataStorage *storage)
{
m_DataStorage = storage;
}
void QmitkLabelSetWidget::OnSearchLabel()
{
std::string text = m_Controls.m_LabelSearchBox->text().toStdString();
int pixelValue = -1;
int row = -1;
for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i)
{
if (m_Controls.m_LabelSetTableWidget->item(i, 0)->text().toStdString().compare(text) == 0)
{
pixelValue = m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt();
row = i;
break;
}
}
if (pixelValue == -1)
{
return;
}
GetWorkingImage()->GetActiveLabelSet()->SetActiveLabel(pixelValue);
QTableWidgetItem *nameItem = m_Controls.m_LabelSetTableWidget->item(row, NAME_COL);
if (!nameItem)
{
return;
}
m_Controls.m_LabelSetTableWidget->clearSelection();
m_Controls.m_LabelSetTableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
m_Controls.m_LabelSetTableWidget->selectRow(row);
m_Controls.m_LabelSetTableWidget->scrollToItem(nameItem);
m_Controls.m_LabelSetTableWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
GetWorkingImage()->GetActiveLabelSet()->SetActiveLabel(pixelValue);
this->WaitCursorOn();
mitk::Point3D pos =
GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetCenterOfMassCoordinates();
m_ToolManager->WorkingDataChanged();
if (pos.GetVnlVector().max_value() > 0.0)
{
emit goToLabel(pos);
}
else
{
GetWorkingImage()->UpdateCenterOfMass(pixelValue, GetWorkingImage()->GetActiveLayer());
mitk::Point3D pos =
GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetCenterOfMassCoordinates();
emit goToLabel(pos);
}
this->WaitCursorOff();
}
void QmitkLabelSetWidget::OnLabelListModified(const QStringList &list)
{
QStringListModel *completeModel = static_cast<QStringListModel *>(m_Completer->model());
completeModel->setStringList(list);
}
mitk::LabelSetImage *QmitkLabelSetWidget::GetWorkingImage()
{
mitk::DataNode *workingNode = GetWorkingNode();
mitk::LabelSetImage *workingImage = dynamic_cast<mitk::LabelSetImage *>(workingNode->GetData());
assert(workingImage);
return workingImage;
}
mitk::DataNode *QmitkLabelSetWidget::GetWorkingNode()
{
mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0);
assert(workingNode);
return workingNode;
}
void QmitkLabelSetWidget::UpdateControls()
{
mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0);
bool hasWorkingData = (workingNode != nullptr);
m_Controls.m_LabelSetTableWidget->setEnabled(hasWorkingData);
m_Controls.m_LabelSearchBox->setEnabled(hasWorkingData);
if (!hasWorkingData)
return;
QStringListModel *completeModel = static_cast<QStringListModel *>(m_Completer->model());
completeModel->setStringList(GetLabelStringList());
}
void QmitkLabelSetWidget::OnCreateCroppedMask(bool)
{
m_ToolManager->ActivateTool(-1);
mitk::LabelSetImage *workingImage = GetWorkingImage();
mitk::Image::Pointer maskImage;
int pixelValue = GetPixelValueOfSelectedItem();
try
{
this->WaitCursorOn();
mitk::AutoCropImageFilter::Pointer cropFilter = mitk::AutoCropImageFilter::New();
cropFilter->SetInput(workingImage->CreateLabelMask(pixelValue));
cropFilter->SetBackgroundValue(0);
cropFilter->SetMarginFactor(1.15);
cropFilter->Update();
maskImage = cropFilter->GetOutput();
this->WaitCursorOff();
}
catch (mitk::Exception &e)
{
this->WaitCursorOff();
MITK_ERROR << "Exception caught: " << e.GetDescription();
QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n");
return;
}
if (maskImage.IsNull())
{
QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n");
return;
}
mitk::DataNode::Pointer maskNode = mitk::DataNode::New();
std::string name = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetName();
name += "-mask";
maskNode->SetName(name);
maskNode->SetData(maskImage);
maskNode->SetBoolProperty("binary", true);
maskNode->SetBoolProperty("outline binary", true);
maskNode->SetBoolProperty("outline binary shadow", true);
maskNode->SetFloatProperty("outline width", 2.0);
maskNode->SetColor(workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetColor());
maskNode->SetOpacity(1.0);
m_DataStorage->Add(maskNode, GetWorkingNode());
}
void QmitkLabelSetWidget::OnCreateMask(bool /*triggered*/)
{
m_ToolManager->ActivateTool(-1);
mitk::LabelSetImage *workingImage = GetWorkingImage();
mitk::Image::Pointer maskImage;
int pixelValue = GetPixelValueOfSelectedItem();
try
{
this->WaitCursorOn();
maskImage = workingImage->CreateLabelMask(pixelValue);
this->WaitCursorOff();
}
catch (mitk::Exception &e)
{
this->WaitCursorOff();
MITK_ERROR << "Exception caught: " << e.GetDescription();
QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n");
return;
}
if (maskImage.IsNull())
{
QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n");
return;
}
mitk::DataNode::Pointer maskNode = mitk::DataNode::New();
std::string name = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetName();
name += "-mask";
maskNode->SetName(name);
maskNode->SetData(maskImage);
maskNode->SetBoolProperty("binary", true);
maskNode->SetBoolProperty("outline binary", true);
maskNode->SetBoolProperty("outline binary shadow", true);
maskNode->SetFloatProperty("outline width", 2.0);
maskNode->SetColor(workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetColor());
maskNode->SetOpacity(1.0);
m_DataStorage->Add(maskNode, GetWorkingNode());
}
void QmitkLabelSetWidget::OnToggleOutline(bool value)
{
mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0);
assert(workingNode);
workingNode->SetBoolProperty("labelset.contour.active", value);
workingNode->GetData()->Modified(); // fixme: workaround to force data-type rendering (and not only property-type)
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkLabelSetWidget::OnCreateSmoothedSurface(bool /*triggered*/)
{
m_ToolManager->ActivateTool(-1);
mitk::DataNode::Pointer workingNode = GetWorkingNode();
mitk::LabelSetImage *workingImage = GetWorkingImage();
int pixelValue = GetPixelValueOfSelectedItem();
mitk::LabelSetImageToSurfaceThreadedFilter::Pointer surfaceFilter = mitk::LabelSetImageToSurfaceThreadedFilter::New();
itk::SimpleMemberCommand<QmitkLabelSetWidget>::Pointer successCommand =
itk::SimpleMemberCommand<QmitkLabelSetWidget>::New();
successCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone);
surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand);
itk::SimpleMemberCommand<QmitkLabelSetWidget>::Pointer errorCommand =
itk::SimpleMemberCommand<QmitkLabelSetWidget>::New();
errorCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone);
surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand);
mitk::DataNode::Pointer groupNode = workingNode;
surfaceFilter->SetPointerParameter("Group node", groupNode);
surfaceFilter->SetPointerParameter("Input", workingImage);
surfaceFilter->SetParameter("RequestedLabel", pixelValue);
surfaceFilter->SetParameter("Smooth", true);
surfaceFilter->SetDataStorage(*m_DataStorage);
mitk::StatusBar::GetInstance()->DisplayText("Surface creation is running in background...");
try
{
surfaceFilter->StartAlgorithm();
}
catch (mitk::Exception &e)
{
MITK_ERROR << "Exception caught: " << e.GetDescription();
QMessageBox::information(this,
"Create Surface",
"Could not create a surface mesh out of the selected label. See error log for details.\n");
}
}
void QmitkLabelSetWidget::OnCreateDetailedSurface(bool /*triggered*/)
{
m_ToolManager->ActivateTool(-1);
mitk::DataNode::Pointer workingNode = GetWorkingNode();
mitk::LabelSetImage *workingImage = GetWorkingImage();
int pixelValue = GetPixelValueOfSelectedItem();
mitk::LabelSetImageToSurfaceThreadedFilter::Pointer surfaceFilter = mitk::LabelSetImageToSurfaceThreadedFilter::New();
itk::SimpleMemberCommand<QmitkLabelSetWidget>::Pointer successCommand =
itk::SimpleMemberCommand<QmitkLabelSetWidget>::New();
successCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone);
surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand);
itk::SimpleMemberCommand<QmitkLabelSetWidget>::Pointer errorCommand =
itk::SimpleMemberCommand<QmitkLabelSetWidget>::New();
errorCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone);
surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand);
mitk::DataNode::Pointer groupNode = workingNode;
surfaceFilter->SetPointerParameter("Group node", groupNode);
surfaceFilter->SetPointerParameter("Input", workingImage);
surfaceFilter->SetParameter("RequestedLabel", pixelValue);
surfaceFilter->SetParameter("Smooth", false);
surfaceFilter->SetDataStorage(*m_DataStorage);
mitk::StatusBar::GetInstance()->DisplayText("Surface creation is running in background...");
try
{
surfaceFilter->StartAlgorithm();
}
catch (mitk::Exception &e)
{
MITK_ERROR << "Exception caught: " << e.GetDescription();
QMessageBox::information(this,
"Create Surface",
"Could not create a surface mesh out of the selected label. See error log for details.\n");
}
}
void QmitkLabelSetWidget::OnImportLabeledImage()
{
/*
m_ToolManager->ActivateTool(-1);
mitk::DataNode* referenceNode = m_ToolManager->GetReferenceData(0);
assert(referenceNode);
// Ask the user for a list of files to open
QStringList fileNames = QFileDialog::getOpenFileNames( this, "Open Image", m_LastFileOpenPath,
mitk::CoreObjectFactory::GetInstance()->GetFileExtensions());
if (fileNames.empty())
return;
try
{
this->WaitCursorOn();
mitk::Image::Pointer image = mitk::IOUtil::Load<mitk::Image>( fileNames.front().toStdString() );
if (image.IsNull())
{
this->WaitCursorOff();
QMessageBox::information(this, "Import Labeled Image", "Could not load the selected segmentation.\n");
return;
}
mitk::LabelSetImage::Pointer newImage = mitk::LabelSetImage::New();
newImage->InitializeByLabeledImage(image);
this->WaitCursorOff();
mitk::DataNode::Pointer newNode = mitk::DataNode::New();
std::string newName = referenceNode->GetName();
newName += "-labels";
newNode->SetName(newName);
newNode->SetData(newImage);
m_DataStorage->Add(newNode, referenceNode);
}
catch (mitk::Exception & e)
{
this->WaitCursorOff();
MITK_ERROR << "Exception caught: " << e.GetDescription();
QMessageBox::information(this, "Import Labeled Image", "Could not load the selected segmentation. See error log
for details.\n");
return;
}
this->UpdateControls();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
*/
}
void QmitkLabelSetWidget::OnImportSegmentation()
{
/*
m_ToolManager->ActivateTool(-1);
mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0);
assert(workingNode);
mitk::LabelSetImage* workingImage = dynamic_cast<mitk::LabelSetImage*>( workingNode->GetData() );
assert(workingImage);
std::string fileExtensions("Segmentation files (*.lset);;");
QString qfileName = QFileDialog::getOpenFileName(this, "Import Segmentation", m_LastFileOpenPath,
fileExtensions.c_str() );
if (qfileName.isEmpty() ) return;
mitk::NrrdLabelSetImageReader::Pointer reader = mitk::NrrdLabelSetImageReader::New();
reader->SetFileName(qfileName.toLatin1());
try
{
this->WaitCursorOn();
reader->Update();
mitk::LabelSetImage::Pointer newImage = reader->GetOutput();
workingImage->Concatenate(newImage);
this->WaitCursorOff();
}
catch ( mitk::Exception& e )
{
this->WaitCursorOff();
MITK_ERROR << "Exception caught: " << e.GetDescription();
QMessageBox::information(this, "Import Segmentation", "Could not import the selected segmentation session.\n See
error log for details.\n");
}
*/
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkLabelSetWidget::WaitCursorOn()
{
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
}
void QmitkLabelSetWidget::WaitCursorOff()
{
this->RestoreOverrideCursor();
}
void QmitkLabelSetWidget::RestoreOverrideCursor()
{
QApplication::restoreOverrideCursor();
}
void QmitkLabelSetWidget::OnThreadedCalculationDone()
{
mitk::StatusBar::GetInstance()->Clear();
}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.h b/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.h
index a122cef784..690186d7c8 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.h
@@ -1,171 +1,171 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef QmitkLabelSetWidget_h
#define QmitkLabelSetWidget_h
#include "MitkSegmentationUIExports.h"
#include "mitkColorSequenceRainbow.h"
#include "mitkLabel.h"
#include "mitkNumericTypes.h"
#include <ui_QmitkLabelSetWidgetControls.h>
class QmitkDataStorageComboBox;
class QCompleter;
namespace mitk
{
class LabelSetImage;
class LabelSet;
class Label;
class DataStorage;
class ToolManager;
class DataNode;
}
class MITKSEGMENTATIONUI_EXPORT QmitkLabelSetWidget : public QWidget
{
Q_OBJECT
public:
explicit QmitkLabelSetWidget(QWidget *parent = nullptr);
~QmitkLabelSetWidget() override;
void SetDataStorage(mitk::DataStorage *storage);
void SetOrganColors(const QStringList &organColors);
void UpdateControls();
virtual void setEnabled(bool enabled);
QStringList &GetLabelStringList();
signals:
/// \brief Send a signal when it was requested to go to a label.
void goToLabel(const mitk::Point3D &);
- void resetView();
+ void LabelSetWidgetReset();
public slots:
/**
* @brief Updates the current labels in the label set widget table. For each label (widget item) the 'UpdateTableWidgetItem' is called.
*
* Updating means setting the color box of the table, setting the column with and fill it with the label name.
* Furthermore the two push buttons for locking and showing/hiding the layer are checked/unchecked.
* This functions only changes the appearance of the table widget and no render window update is necessary.
*/
void UpdateAllTableWidgetItems();
/**
* @brief Resets the current labels in the label set widget table. For each label a widget item is inserted into the table.
*
* Resetting means removing all rows of the widget table and inserting new rows (labels) from the active label set (= layer) of the current working node.
* The currently active label is selected and 'Number of labels' is set.
* As this function is typically used after one label has been removed or the reference node has been changed (e.g.) the render windows have to be updated.
*/
void ResetAllTableWidgetItems();
void SelectLabelByPixelValue(mitk::Label::PixelType pixelValue);
private slots:
// LabelSet dependent
void OnOpacityChanged(int);
void OnUnlockAllLabels(bool);
void OnLockAllLabels(bool);
void OnSetAllLabelsVisible(bool);
void OnSetAllLabelsInvisible(bool);
void OnSetOnlyActiveLabelVisible(bool);
void OnRandomColor(bool);
void OnRemoveLabel(bool);
void OnRemoveLabels(bool);
void OnRenameLabel(bool);
void OnLockedButtonClicked();
void OnVisibleButtonClicked();
void OnColorButtonClicked();
void OnItemClicked(QTableWidgetItem *item);
void OnItemDoubleClicked(QTableWidgetItem *item);
void OnTableViewContextMenuRequested(const QPoint &);
void InsertTableWidgetItem(mitk::Label *label);
void UpdateTableWidgetItem(QTableWidgetItem *item);
// reaction to "returnPressed" signal from ...
void OnSearchLabel();
// reaction to the button "Change Label"
void OnActiveLabelChanged(int pixelValue);
// LabelSetImage Dependet
void OnCreateDetailedSurface(bool);
void OnCreateSmoothedSurface(bool);
// reaction to the signal "createMask" from QmitkLabelSetTableWidget
void OnCreateMask(bool);
void OnCreateMasks(bool);
// reaction to the signal "createCroppedMask" from QmitkLabelSetTableWidget
void OnCreateCroppedMask(bool);
void OnCombineAndCreateMask(bool);
void OnCombineAndCreateSurface(bool);
void OnEraseLabel(bool);
void OnEraseLabels(bool);
// reaction to signal "mergeLabel" from QmitkLabelSetTableWidget
void OnMergeLabel(bool);
void OnMergeLabels(bool);
// reaction to the button "Import Segmentation"
void OnImportSegmentation();
// reaction to the button "Import Labeled Image"
void OnImportLabeledImage();
// reaction to signal "labelListModified" from QmitkLabelSetTableWidget
void OnLabelListModified(const QStringList &list);
// reaction to the signal "toggleOutline" from QmitkLabelSetTableWidget
void OnToggleOutline(bool);
private:
enum TableColumns
{
NAME_COL = 0,
LOCKED_COL,
COLOR_COL,
VISIBLE_COL
};
void WaitCursorOn();
void WaitCursorOff();
void RestoreOverrideCursor();
void OnThreadedCalculationDone();
void InitializeTableWidget();
int GetPixelValueOfSelectedItem();
mitk::LabelSetImage *GetWorkingImage();
mitk::DataNode *GetWorkingNode();
Ui::QmitkLabelSetWidgetControls m_Controls;
mitk::ColorSequenceRainbow m_ColorSequenceRainbow;
mitk::DataStorage *m_DataStorage;
QCompleter *m_Completer;
mitk::ToolManager *m_ToolManager;
QStringList m_OrganColors;
QStringList m_LabelStringList;
};
#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp b/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp
index 0090146b9c..e41f25d83b 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp
@@ -1,1442 +1,1442 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "QmitkSlicesInterpolator.h"
#include "QmitkSelectableGLWidget.h"
#include "QmitkStdMultiWidget.h"
#include "mitkApplyDiffImageOperation.h"
#include "mitkColorProperty.h"
#include "mitkCoreObjectFactory.h"
#include "mitkDiffImageApplier.h"
#include "mitkInteractionConst.h"
#include "mitkLevelWindowProperty.h"
#include "mitkOperationEvent.h"
#include "mitkProgressBar.h"
#include "mitkProperties.h"
#include "mitkRenderingManager.h"
#include "mitkSegTool2D.h"
#include "mitkSliceNavigationController.h"
#include "mitkSurfaceToImageFilter.h"
#include "mitkToolManager.h"
#include "mitkUndoController.h"
#include <mitkExtractSliceFilter.h>
#include <mitkImageReadAccessor.h>
#include <mitkImageTimeSelector.h>
#include <mitkImageWriteAccessor.h>
#include <mitkPlaneProposer.h>
#include <mitkUnstructuredGridClusteringFilter.h>
#include <mitkVtkImageOverwrite.h>
#include <mitkShapeBasedInterpolationAlgorithm.h>
#include <itkCommand.h>
#include <QCheckBox>
#include <QCursor>
#include <QMenu>
#include <QMessageBox>
#include <QPushButton>
#include <QVBoxLayout>
#include <vtkPolyVertex.h>
#include <vtkUnstructuredGrid.h>
#include <array>
#include <atomic>
#include <thread>
#include <vector>
namespace
{
template <typename T = mitk::BaseData>
itk::SmartPointer<T> GetData(const mitk::DataNode* dataNode)
{
return nullptr != dataNode
? dynamic_cast<T*>(dataNode->GetData())
: nullptr;
}
}
float SURFACE_COLOR_RGB[3] = {0.49f, 1.0f, 0.16f};
const std::map<QAction *, mitk::SliceNavigationController *> QmitkSlicesInterpolator::createActionToSliceDimension()
{
std::map<QAction *, mitk::SliceNavigationController *> actionToSliceDimension;
foreach (mitk::SliceNavigationController *slicer, m_ControllerToDeleteObserverTag.keys())
{
actionToSliceDimension[new QAction(QString::fromStdString(slicer->GetViewDirectionAsString()), nullptr)] = slicer;
}
return actionToSliceDimension;
}
QmitkSlicesInterpolator::QmitkSlicesInterpolator(QWidget *parent, const char * /*name*/)
: QWidget(parent),
// ACTION_TO_SLICEDIMENSION( createActionToSliceDimension() ),
m_Interpolator(mitk::SegmentationInterpolationController::New()),
m_SurfaceInterpolator(mitk::SurfaceInterpolationController::GetInstance()),
m_ToolManager(nullptr),
m_Initialized(false),
m_LastSNC(nullptr),
m_LastSliceIndex(0),
m_2DInterpolationEnabled(false),
m_3DInterpolationEnabled(false),
m_FirstRun(true)
{
m_GroupBoxEnableExclusiveInterpolationMode = new QGroupBox("Interpolation", this);
QVBoxLayout *vboxLayout = new QVBoxLayout(m_GroupBoxEnableExclusiveInterpolationMode);
m_EdgeDetector = mitk::FeatureBasedEdgeDetectionFilter::New();
m_PointScorer = mitk::PointCloudScoringFilter::New();
m_CmbInterpolation = new QComboBox(m_GroupBoxEnableExclusiveInterpolationMode);
m_CmbInterpolation->addItem("Disabled");
m_CmbInterpolation->addItem("2-Dimensional");
m_CmbInterpolation->addItem("3-Dimensional");
vboxLayout->addWidget(m_CmbInterpolation);
m_BtnApply2D = new QPushButton("Confirm for single slice", m_GroupBoxEnableExclusiveInterpolationMode);
vboxLayout->addWidget(m_BtnApply2D);
m_BtnApplyForAllSlices2D = new QPushButton("Confirm for all slices", m_GroupBoxEnableExclusiveInterpolationMode);
vboxLayout->addWidget(m_BtnApplyForAllSlices2D);
m_BtnApply3D = new QPushButton("Confirm", m_GroupBoxEnableExclusiveInterpolationMode);
vboxLayout->addWidget(m_BtnApply3D);
// T28261
// m_BtnSuggestPlane = new QPushButton("Suggest a plane", m_GroupBoxEnableExclusiveInterpolationMode);
// vboxLayout->addWidget(m_BtnSuggestPlane);
m_BtnReinit3DInterpolation = new QPushButton("Reinit Interpolation", m_GroupBoxEnableExclusiveInterpolationMode);
vboxLayout->addWidget(m_BtnReinit3DInterpolation);
m_ChkShowPositionNodes = new QCheckBox("Show Position Nodes", m_GroupBoxEnableExclusiveInterpolationMode);
vboxLayout->addWidget(m_ChkShowPositionNodes);
this->HideAllInterpolationControls();
connect(m_CmbInterpolation, SIGNAL(currentIndexChanged(int)), this, SLOT(OnInterpolationMethodChanged(int)));
connect(m_BtnApply2D, SIGNAL(clicked()), this, SLOT(OnAcceptInterpolationClicked()));
connect(m_BtnApplyForAllSlices2D, SIGNAL(clicked()), this, SLOT(OnAcceptAllInterpolationsClicked()));
connect(m_BtnApply3D, SIGNAL(clicked()), this, SLOT(OnAccept3DInterpolationClicked()));
// T28261
// connect(m_BtnSuggestPlane, SIGNAL(clicked()), this, SLOT(OnSuggestPlaneClicked()));
connect(m_BtnReinit3DInterpolation, SIGNAL(clicked()), this, SLOT(OnReinit3DInterpolation()));
connect(m_ChkShowPositionNodes, SIGNAL(toggled(bool)), this, SLOT(OnShowMarkers(bool)));
connect(m_ChkShowPositionNodes, SIGNAL(toggled(bool)), this, SIGNAL(SignalShowMarkerNodes(bool)));
QHBoxLayout *layout = new QHBoxLayout(this);
layout->addWidget(m_GroupBoxEnableExclusiveInterpolationMode);
this->setLayout(layout);
itk::ReceptorMemberCommand<QmitkSlicesInterpolator>::Pointer command =
itk::ReceptorMemberCommand<QmitkSlicesInterpolator>::New();
command->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnInterpolationInfoChanged);
InterpolationInfoChangedObserverTag = m_Interpolator->AddObserver(itk::ModifiedEvent(), command);
itk::ReceptorMemberCommand<QmitkSlicesInterpolator>::Pointer command2 =
itk::ReceptorMemberCommand<QmitkSlicesInterpolator>::New();
command2->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged);
SurfaceInterpolationInfoChangedObserverTag = m_SurfaceInterpolator->AddObserver(itk::ModifiedEvent(), command2);
auto command3 = itk::ReceptorMemberCommand<QmitkSlicesInterpolator>::New();
command3->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnInterpolationAborted);
InterpolationAbortedObserverTag = m_Interpolator->AddObserver(itk::AbortEvent(), command3);
// feedback node and its visualization properties
m_FeedbackNode = mitk::DataNode::New();
mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties(m_FeedbackNode);
m_FeedbackNode->SetProperty("binary", mitk::BoolProperty::New(true));
m_FeedbackNode->SetProperty("outline binary", mitk::BoolProperty::New(true));
m_FeedbackNode->SetProperty("color", mitk::ColorProperty::New(255.0, 255.0, 0.0));
m_FeedbackNode->SetProperty("texture interpolation", mitk::BoolProperty::New(false));
m_FeedbackNode->SetProperty("layer", mitk::IntProperty::New(20));
m_FeedbackNode->SetProperty("levelwindow", mitk::LevelWindowProperty::New(mitk::LevelWindow(0, 1)));
m_FeedbackNode->SetProperty("name", mitk::StringProperty::New("Interpolation feedback"));
m_FeedbackNode->SetProperty("opacity", mitk::FloatProperty::New(0.8));
m_FeedbackNode->SetProperty("helper object", mitk::BoolProperty::New(true));
m_InterpolatedSurfaceNode = mitk::DataNode::New();
m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(SURFACE_COLOR_RGB));
m_InterpolatedSurfaceNode->SetProperty("name", mitk::StringProperty::New("Surface Interpolation feedback"));
m_InterpolatedSurfaceNode->SetProperty("opacity", mitk::FloatProperty::New(0.5));
m_InterpolatedSurfaceNode->SetProperty("line width", mitk::FloatProperty::New(4.0f));
m_InterpolatedSurfaceNode->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false));
m_InterpolatedSurfaceNode->SetProperty("helper object", mitk::BoolProperty::New(true));
m_InterpolatedSurfaceNode->SetVisibility(false);
m_3DContourNode = mitk::DataNode::New();
m_3DContourNode->SetProperty("color", mitk::ColorProperty::New(0.0, 0.0, 0.0));
m_3DContourNode->SetProperty("hidden object", mitk::BoolProperty::New(true));
m_3DContourNode->SetProperty("name", mitk::StringProperty::New("Drawn Contours"));
m_3DContourNode->SetProperty("material.representation", mitk::VtkRepresentationProperty::New(VTK_WIREFRAME));
m_3DContourNode->SetProperty("material.wireframeLineWidth", mitk::FloatProperty::New(2.0f));
m_3DContourNode->SetProperty("3DContourContainer", mitk::BoolProperty::New(true));
m_3DContourNode->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false));
m_3DContourNode->SetVisibility(
false, mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget0")));
m_3DContourNode->SetVisibility(
false, mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1")));
m_3DContourNode->SetVisibility(
false, mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget2")));
m_3DContourNode->SetVisibility(
false, mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3")));
QWidget::setContentsMargins(0, 0, 0, 0);
if (QWidget::layout() != nullptr)
{
QWidget::layout()->setContentsMargins(0, 0, 0, 0);
}
// For running 3D Interpolation in background
// create a QFuture and a QFutureWatcher
connect(&m_Watcher, SIGNAL(started()), this, SLOT(StartUpdateInterpolationTimer()));
connect(&m_Watcher, SIGNAL(finished()), this, SLOT(OnSurfaceInterpolationFinished()));
connect(&m_Watcher, SIGNAL(finished()), this, SLOT(StopUpdateInterpolationTimer()));
m_Timer = new QTimer(this);
connect(m_Timer, SIGNAL(timeout()), this, SLOT(ChangeSurfaceColor()));
}
void QmitkSlicesInterpolator::SetDataStorage(mitk::DataStorage::Pointer storage)
{
if (m_DataStorage == storage)
{
return;
}
if (m_DataStorage.IsNotNull())
{
m_DataStorage->RemoveNodeEvent.RemoveListener(
mitk::MessageDelegate1<QmitkSlicesInterpolator, const mitk::DataNode*>(this, &QmitkSlicesInterpolator::NodeRemoved)
);
}
m_DataStorage = storage;
m_SurfaceInterpolator->SetDataStorage(storage);
if (m_DataStorage.IsNotNull())
{
m_DataStorage->RemoveNodeEvent.AddListener(
mitk::MessageDelegate1<QmitkSlicesInterpolator, const mitk::DataNode*>(this, &QmitkSlicesInterpolator::NodeRemoved)
);
}
}
mitk::DataStorage *QmitkSlicesInterpolator::GetDataStorage()
{
if (m_DataStorage.IsNotNull())
{
return m_DataStorage;
}
else
{
return nullptr;
}
}
void QmitkSlicesInterpolator::Initialize(mitk::ToolManager *toolManager,
const QList<mitk::SliceNavigationController *> &controllers)
{
Q_ASSERT(!controllers.empty());
if (m_Initialized)
{
// remove old observers
Uninitialize();
}
m_ToolManager = toolManager;
if (m_ToolManager)
{
// set enabled only if a segmentation is selected
mitk::DataNode *node = m_ToolManager->GetWorkingData(0);
QWidget::setEnabled(node != nullptr);
// react whenever the set of selected segmentation changes
m_ToolManager->WorkingDataChanged +=
mitk::MessageDelegate<QmitkSlicesInterpolator>(this, &QmitkSlicesInterpolator::OnToolManagerWorkingDataModified);
m_ToolManager->ReferenceDataChanged += mitk::MessageDelegate<QmitkSlicesInterpolator>(
this, &QmitkSlicesInterpolator::OnToolManagerReferenceDataModified);
// connect to the slice navigation controller. after each change, call the interpolator
foreach (mitk::SliceNavigationController *slicer, controllers)
{
// Has to be initialized
m_LastSNC = slicer;
m_TimePoints.insert(slicer, slicer->GetSelectedTimePoint());
itk::MemberCommand<QmitkSlicesInterpolator>::Pointer deleteCommand =
itk::MemberCommand<QmitkSlicesInterpolator>::New();
deleteCommand->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnSliceNavigationControllerDeleted);
m_ControllerToDeleteObserverTag.insert(slicer, slicer->AddObserver(itk::DeleteEvent(), deleteCommand));
itk::MemberCommand<QmitkSlicesInterpolator>::Pointer timeChangedCommand =
itk::MemberCommand<QmitkSlicesInterpolator>::New();
timeChangedCommand->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnTimeChanged);
m_ControllerToTimeObserverTag.insert(
slicer, slicer->AddObserver(mitk::SliceNavigationController::TimeGeometryEvent(nullptr, 0), timeChangedCommand));
itk::MemberCommand<QmitkSlicesInterpolator>::Pointer sliceChangedCommand =
itk::MemberCommand<QmitkSlicesInterpolator>::New();
sliceChangedCommand->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnSliceChanged);
m_ControllerToSliceObserverTag.insert(
slicer, slicer->AddObserver(mitk::SliceNavigationController::GeometrySliceEvent(nullptr, 0), sliceChangedCommand));
}
ACTION_TO_SLICEDIMENSION = createActionToSliceDimension();
}
m_Initialized = true;
}
void QmitkSlicesInterpolator::Uninitialize()
{
if (m_ToolManager.IsNotNull())
{
m_ToolManager->WorkingDataChanged -=
mitk::MessageDelegate<QmitkSlicesInterpolator>(this, &QmitkSlicesInterpolator::OnToolManagerWorkingDataModified);
m_ToolManager->ReferenceDataChanged -= mitk::MessageDelegate<QmitkSlicesInterpolator>(
this, &QmitkSlicesInterpolator::OnToolManagerReferenceDataModified);
}
foreach (mitk::SliceNavigationController *slicer, m_ControllerToSliceObserverTag.keys())
{
slicer->RemoveObserver(m_ControllerToDeleteObserverTag.take(slicer));
slicer->RemoveObserver(m_ControllerToTimeObserverTag.take(slicer));
slicer->RemoveObserver(m_ControllerToSliceObserverTag.take(slicer));
}
ACTION_TO_SLICEDIMENSION.clear();
m_ToolManager = nullptr;
m_Initialized = false;
}
QmitkSlicesInterpolator::~QmitkSlicesInterpolator()
{
if (m_Initialized)
{
// remove old observers
Uninitialize();
}
WaitForFutures();
if (m_DataStorage.IsNotNull())
{
m_DataStorage->RemoveNodeEvent.RemoveListener(
mitk::MessageDelegate1<QmitkSlicesInterpolator, const mitk::DataNode*>(this, &QmitkSlicesInterpolator::NodeRemoved)
);
if (m_DataStorage->Exists(m_3DContourNode))
m_DataStorage->Remove(m_3DContourNode);
if (m_DataStorage->Exists(m_InterpolatedSurfaceNode))
m_DataStorage->Remove(m_InterpolatedSurfaceNode);
}
// remove observer
m_Interpolator->RemoveObserver(InterpolationAbortedObserverTag);
m_Interpolator->RemoveObserver(InterpolationInfoChangedObserverTag);
m_SurfaceInterpolator->RemoveObserver(SurfaceInterpolationInfoChangedObserverTag);
delete m_Timer;
}
/**
External enableization...
*/
void QmitkSlicesInterpolator::setEnabled(bool enable)
{
QWidget::setEnabled(enable);
// Set the gui elements of the different interpolation modi enabled
if (enable)
{
if (m_2DInterpolationEnabled)
{
this->Show2DInterpolationControls(true);
m_Interpolator->Activate2DInterpolation(true);
}
else if (m_3DInterpolationEnabled)
{
this->Show3DInterpolationControls(true);
this->Show3DInterpolationResult(true);
}
}
// Set all gui elements of the interpolation disabled
else
{
this->HideAllInterpolationControls();
this->Show3DInterpolationResult(false);
}
}
void QmitkSlicesInterpolator::On2DInterpolationEnabled(bool status)
{
OnInterpolationActivated(status);
m_Interpolator->Activate2DInterpolation(status);
}
void QmitkSlicesInterpolator::On3DInterpolationEnabled(bool status)
{
On3DInterpolationActivated(status);
}
void QmitkSlicesInterpolator::OnInterpolationDisabled(bool status)
{
if (status)
{
OnInterpolationActivated(!status);
On3DInterpolationActivated(!status);
this->Show3DInterpolationResult(false);
}
}
void QmitkSlicesInterpolator::HideAllInterpolationControls()
{
this->Show2DInterpolationControls(false);
this->Show3DInterpolationControls(false);
}
void QmitkSlicesInterpolator::Show2DInterpolationControls(bool show)
{
m_BtnApply2D->setVisible(show);
m_BtnApplyForAllSlices2D->setVisible(show);
}
void QmitkSlicesInterpolator::Show3DInterpolationControls(bool show)
{
m_BtnApply3D->setVisible(show);
// T28261
// m_BtnSuggestPlane->setVisible(show);
m_ChkShowPositionNodes->setVisible(show);
m_BtnReinit3DInterpolation->setVisible(show);
}
void QmitkSlicesInterpolator::OnInterpolationMethodChanged(int index)
{
switch (index)
{
case 0: // Disabled
m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation");
this->HideAllInterpolationControls();
this->OnInterpolationActivated(false);
this->On3DInterpolationActivated(false);
this->Show3DInterpolationResult(false);
m_Interpolator->Activate2DInterpolation(false);
break;
case 1: // 2D
m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation (Enabled)");
this->HideAllInterpolationControls();
this->Show2DInterpolationControls(true);
this->OnInterpolationActivated(true);
this->On3DInterpolationActivated(false);
m_Interpolator->Activate2DInterpolation(true);
break;
case 2: // 3D
m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation (Enabled)");
this->HideAllInterpolationControls();
this->Show3DInterpolationControls(true);
this->OnInterpolationActivated(false);
this->On3DInterpolationActivated(true);
m_Interpolator->Activate2DInterpolation(false);
break;
default:
MITK_ERROR << "Unknown interpolation method!";
m_CmbInterpolation->setCurrentIndex(0);
break;
}
}
void QmitkSlicesInterpolator::OnShowMarkers(bool state)
{
mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers =
m_DataStorage->GetSubset(mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true)));
for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End();
++it)
{
it->Value()->SetProperty("helper object", mitk::BoolProperty::New(!state));
}
}
void QmitkSlicesInterpolator::OnToolManagerWorkingDataModified()
{
if (m_ToolManager->GetWorkingData(0) != nullptr)
{
m_Segmentation = dynamic_cast<mitk::Image *>(m_ToolManager->GetWorkingData(0)->GetData());
m_BtnReinit3DInterpolation->setEnabled(true);
}
else
{
// If no workingdata is set, remove the interpolation feedback
this->GetDataStorage()->Remove(m_FeedbackNode);
m_FeedbackNode->SetData(nullptr);
this->GetDataStorage()->Remove(m_3DContourNode);
m_3DContourNode->SetData(nullptr);
this->GetDataStorage()->Remove(m_InterpolatedSurfaceNode);
m_InterpolatedSurfaceNode->SetData(nullptr);
m_BtnReinit3DInterpolation->setEnabled(false);
return;
}
// Updating the current selected segmentation for the 3D interpolation
SetCurrentContourListID();
if (m_2DInterpolationEnabled)
{
OnInterpolationActivated(true); // re-initialize if needed
}
this->CheckSupportedImageDimension();
}
void QmitkSlicesInterpolator::OnToolManagerReferenceDataModified()
{
}
void QmitkSlicesInterpolator::OnTimeChanged(itk::Object *sender, const itk::EventObject &e)
{
// Check if we really have a GeometryTimeEvent
if (!dynamic_cast<const mitk::SliceNavigationController::GeometryTimeEvent *>(&e))
return;
mitk::SliceNavigationController *slicer = dynamic_cast<mitk::SliceNavigationController *>(sender);
Q_ASSERT(slicer);
const auto timePoint = slicer->GetSelectedTimePoint();
m_TimePoints[slicer] = timePoint;
m_SurfaceInterpolator->SetCurrentTimePoint(timePoint);
if (m_LastSNC == slicer)
{
slicer->SendSlice(); // will trigger a new interpolation
}
}
void QmitkSlicesInterpolator::OnSliceChanged(itk::Object *sender, const itk::EventObject &e)
{
// Check whether we really have a GeometrySliceEvent
if (!dynamic_cast<const mitk::SliceNavigationController::GeometrySliceEvent *>(&e))
return;
mitk::SliceNavigationController *slicer = dynamic_cast<mitk::SliceNavigationController *>(sender);
if (TranslateAndInterpolateChangedSlice(e, slicer))
{
slicer->GetRenderer()->RequestUpdate();
}
}
bool QmitkSlicesInterpolator::TranslateAndInterpolateChangedSlice(const itk::EventObject &e,
mitk::SliceNavigationController *slicer)
{
if (!m_2DInterpolationEnabled)
return false;
try
{
const mitk::SliceNavigationController::GeometrySliceEvent &event =
dynamic_cast<const mitk::SliceNavigationController::GeometrySliceEvent &>(e);
mitk::TimeGeometry *tsg = event.GetTimeGeometry();
if (tsg && m_TimePoints.contains(slicer) && tsg->IsValidTimePoint(m_TimePoints[slicer]))
{
mitk::SlicedGeometry3D *slicedGeometry =
dynamic_cast<mitk::SlicedGeometry3D *>(tsg->GetGeometryForTimePoint(m_TimePoints[slicer]).GetPointer());
if (slicedGeometry)
{
m_LastSNC = slicer;
mitk::PlaneGeometry *plane =
dynamic_cast<mitk::PlaneGeometry *>(slicedGeometry->GetPlaneGeometry(event.GetPos()));
if (plane)
Interpolate(plane, m_TimePoints[slicer], slicer);
return true;
}
}
}
catch (const std::bad_cast &)
{
return false; // so what
}
return false;
}
void QmitkSlicesInterpolator::Interpolate(mitk::PlaneGeometry *plane,
mitk::TimePointType timePoint,
mitk::SliceNavigationController *slicer)
{
if (m_ToolManager)
{
mitk::DataNode *node = m_ToolManager->GetWorkingData(0);
if (node)
{
m_Segmentation = dynamic_cast<mitk::Image *>(node->GetData());
if (m_Segmentation)
{
if (!m_Segmentation->GetTimeGeometry()->IsValidTimePoint(timePoint))
{
MITK_WARN << "Cannot interpolate segmentation. Passed time point is not within the time bounds of WorkingImage. Time point: " << timePoint;
return;
}
const auto timeStep = m_Segmentation->GetTimeGeometry()->TimePointToTimeStep(timePoint);
int clickedSliceDimension(-1);
int clickedSliceIndex(-1);
// calculate real slice position, i.e. slice of the image
mitk::SegTool2D::DetermineAffectedImageSlice(m_Segmentation, plane, clickedSliceDimension, clickedSliceIndex);
mitk::Image::Pointer interpolation =
m_Interpolator->Interpolate(clickedSliceDimension, clickedSliceIndex, plane, timeStep);
m_FeedbackNode->SetData(interpolation);
m_LastSNC = slicer;
m_LastSliceIndex = clickedSliceIndex;
}
}
}
}
void QmitkSlicesInterpolator::OnSurfaceInterpolationFinished()
{
mitk::Surface::Pointer interpolatedSurface = m_SurfaceInterpolator->GetInterpolationResult();
mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0);
if (interpolatedSurface.IsNotNull() && workingNode &&
workingNode->IsVisible(
mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget2"))))
{
m_BtnApply3D->setEnabled(true);
// T28261
// m_BtnSuggestPlane->setEnabled(true);
m_InterpolatedSurfaceNode->SetData(interpolatedSurface);
m_3DContourNode->SetData(m_SurfaceInterpolator->GetContoursAsSurface());
this->Show3DInterpolationResult(true);
if (!m_DataStorage->Exists(m_InterpolatedSurfaceNode))
{
m_DataStorage->Add(m_InterpolatedSurfaceNode);
}
if (!m_DataStorage->Exists(m_3DContourNode))
{
m_DataStorage->Add(m_3DContourNode, workingNode);
}
}
else if (interpolatedSurface.IsNull())
{
m_BtnApply3D->setEnabled(false);
// T28261
// m_BtnSuggestPlane->setEnabled(false);
if (m_DataStorage->Exists(m_InterpolatedSurfaceNode))
{
this->Show3DInterpolationResult(false);
}
}
m_BtnReinit3DInterpolation->setEnabled(true);
foreach (mitk::SliceNavigationController *slicer, m_ControllerToTimeObserverTag.keys())
{
slicer->GetRenderer()->RequestUpdate();
}
}
void QmitkSlicesInterpolator::OnAcceptInterpolationClicked()
{
if (m_Segmentation && m_FeedbackNode->GetData())
{
// Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk
// reslicer
vtkSmartPointer<mitkVtkImageOverwrite> reslice = vtkSmartPointer<mitkVtkImageOverwrite>::New();
// Set slice as input
mitk::Image::Pointer slice = dynamic_cast<mitk::Image *>(m_FeedbackNode->GetData());
reslice->SetInputSlice(slice->GetSliceData()->GetVtkImageAccessor(slice)->GetVtkImageData());
// set overwrite mode to true to write back to the image volume
reslice->SetOverwriteMode(true);
reslice->Modified();
const auto timePoint = m_LastSNC->GetSelectedTimePoint();
if (!m_Segmentation->GetTimeGeometry()->IsValidTimePoint(timePoint))
{
MITK_WARN << "Cannot accept interpolation. Time point selected by SliceNavigationController is not within the time bounds of segmentation. Time point: " << timePoint;
return;
}
mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice);
extractor->SetInput(m_Segmentation);
const auto timeStep = m_Segmentation->GetTimeGeometry()->TimePointToTimeStep(timePoint);
extractor->SetTimeStep(timeStep);
extractor->SetWorldGeometry(m_LastSNC->GetCurrentPlaneGeometry());
extractor->SetVtkOutputRequest(true);
extractor->SetResliceTransformByGeometry(m_Segmentation->GetTimeGeometry()->GetGeometryForTimeStep(timeStep));
extractor->Modified();
extractor->Update();
// the image was modified within the pipeline, but not marked so
m_Segmentation->Modified();
m_Segmentation->GetVtkImageData()->Modified();
m_FeedbackNode->SetData(nullptr);
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkSlicesInterpolator::AcceptAllInterpolations(mitk::SliceNavigationController *slicer)
{
/*
* What exactly is done here:
* 1. We create an empty diff image for the current segmentation
* 2. All interpolated slices are written into the diff image
* 3. Then the diffimage is applied to the original segmentation
*/
if (m_Segmentation)
{
mitk::Image::Pointer segmentation3D = m_Segmentation;
unsigned int timeStep = 0;
const auto timePoint = slicer->GetSelectedTimePoint();
if (4 == m_Segmentation->GetDimension())
{
const auto* geometry = m_Segmentation->GetTimeGeometry();
if (!geometry->IsValidTimePoint(timePoint))
{
MITK_WARN << "Cannot accept all interpolations. Time point selected by passed SliceNavigationController is not within the time bounds of segmentation. Time point: " << timePoint;
return;
}
timeStep = geometry->TimePointToTimeStep(timePoint);
auto timeSelector = mitk::ImageTimeSelector::New();
timeSelector->SetInput(m_Segmentation);
timeSelector->SetTimeNr(timeStep);
timeSelector->Update();
segmentation3D = timeSelector->GetOutput();
}
// Create an empty diff image for the undo operation
auto diffImage = mitk::Image::New();
diffImage->Initialize(segmentation3D);
// Create scope for ImageWriteAccessor so that the accessor is destroyed right after use
{
mitk::ImageWriteAccessor accessor(diffImage);
// Set all pixels to zero
auto pixelType = mitk::MakeScalarPixelType<mitk::Tool::DefaultSegmentationDataType>();
// For legacy purpose support former pixel type of segmentations (before multilabel)
- if (itk::ImageIOBase::UCHAR == m_Segmentation->GetImageDescriptor()->GetChannelDescriptor().GetPixelType().GetComponentType())
+ if (itk::IOComponentEnum::UCHAR == m_Segmentation->GetImageDescriptor()->GetChannelDescriptor().GetPixelType().GetComponentType())
pixelType = mitk::MakeScalarPixelType<unsigned char>();
memset(accessor.GetData(), 0, pixelType.GetSize() * diffImage->GetDimension(0) * diffImage->GetDimension(1) * diffImage->GetDimension(2));
}
// Since we need to shift the plane it must be clone so that the original plane isn't altered
auto slicedGeometry = m_Segmentation->GetSlicedGeometry();
auto planeGeometry = slicer->GetCurrentPlaneGeometry()->Clone();
int sliceDimension = -1;
int sliceIndex = -1;
mitk::SegTool2D::DetermineAffectedImageSlice(m_Segmentation, planeGeometry, sliceDimension, sliceIndex);
const auto numSlices = m_Segmentation->GetDimension(sliceDimension);
mitk::ProgressBar::GetInstance()->AddStepsToDo(numSlices);
std::atomic_uint totalChangedSlices;
// Reuse interpolation algorithm instance for each slice to cache boundary calculations
auto algorithm = mitk::ShapeBasedInterpolationAlgorithm::New();
// Distribute slice interpolations to multiple threads
const auto numThreads = std::min(std::thread::hardware_concurrency(), numSlices);
std::vector<std::vector<unsigned int>> sliceIndices(numThreads);
for (std::remove_const_t<decltype(numSlices)> sliceIndex = 0; sliceIndex < numSlices; ++sliceIndex)
sliceIndices[sliceIndex % numThreads].push_back(sliceIndex);
std::vector<std::thread> threads;
threads.reserve(numThreads);
// This lambda will be executed by the threads
auto interpolate = [=, &interpolator = m_Interpolator, &totalChangedSlices](unsigned int threadIndex)
{
auto clonedPlaneGeometry = planeGeometry->Clone();
auto origin = clonedPlaneGeometry->GetOrigin();
for (auto sliceIndex : sliceIndices[threadIndex])
{
slicedGeometry->WorldToIndex(origin, origin);
origin[sliceDimension] = sliceIndex;
slicedGeometry->IndexToWorld(origin, origin);
clonedPlaneGeometry->SetOrigin(origin);
auto interpolation = interpolator->Interpolate(sliceDimension, sliceIndex, clonedPlaneGeometry, timeStep, algorithm);
if (interpolation.IsNotNull())
{
// Setting up the reslicing pipeline which allows us to write the interpolation results back into the image volume
auto reslicer = vtkSmartPointer<mitkVtkImageOverwrite>::New();
// Set overwrite mode to true to write back to the image volume
reslicer->SetInputSlice(interpolation->GetSliceData()->GetVtkImageAccessor(interpolation)->GetVtkImageData());
reslicer->SetOverwriteMode(true);
reslicer->Modified();
auto diffSliceWriter = mitk::ExtractSliceFilter::New(reslicer);
diffSliceWriter->SetInput(diffImage);
diffSliceWriter->SetTimeStep(0);
diffSliceWriter->SetWorldGeometry(clonedPlaneGeometry);
diffSliceWriter->SetVtkOutputRequest(true);
diffSliceWriter->SetResliceTransformByGeometry(diffImage->GetTimeGeometry()->GetGeometryForTimeStep(0));
diffSliceWriter->Modified();
diffSliceWriter->Update();
++totalChangedSlices;
}
mitk::ProgressBar::GetInstance()->Progress();
}
};
m_Interpolator->EnableSliceImageCache();
for (std::remove_const_t<decltype(numThreads)> threadIndex = 0; threadIndex < numThreads; ++threadIndex)
threads.emplace_back(interpolate, threadIndex); // Run the interpolation
for (auto& thread : threads)
thread.join();
m_Interpolator->DisableSliceImageCache();
if (totalChangedSlices > 0)
{
// Create do/undo operations
auto* doOp = new mitk::ApplyDiffImageOperation(mitk::OpTEST, m_Segmentation, diffImage, timeStep);
auto* undoOp = new mitk::ApplyDiffImageOperation(mitk::OpTEST, m_Segmentation, diffImage, timeStep);
undoOp->SetFactor(-1.0);
auto comment = "Confirm all interpolations (" + std::to_string(totalChangedSlices) + ")";
auto* undoStackItem = new mitk::OperationEvent(mitk::DiffImageApplier::GetInstanceForUndo(), doOp, undoOp, comment);
mitk::OperationEvent::IncCurrGroupEventId();
mitk::OperationEvent::IncCurrObjectEventId();
mitk::UndoController::GetCurrentUndoModel()->SetOperationEvent(undoStackItem);
// Apply the changes to the original image
mitk::DiffImageApplier::GetInstanceForUndo()->ExecuteOperation(doOp);
}
m_FeedbackNode->SetData(nullptr);
}
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkSlicesInterpolator::FinishInterpolation(mitk::SliceNavigationController *slicer)
{
// this redirect is for calling from outside
if (slicer == nullptr)
OnAcceptAllInterpolationsClicked();
else
AcceptAllInterpolations(slicer);
}
void QmitkSlicesInterpolator::OnAcceptAllInterpolationsClicked()
{
QMenu orientationPopup(this);
std::map<QAction *, mitk::SliceNavigationController *>::const_iterator it;
for (it = ACTION_TO_SLICEDIMENSION.begin(); it != ACTION_TO_SLICEDIMENSION.end(); it++)
orientationPopup.addAction(it->first);
connect(&orientationPopup, SIGNAL(triggered(QAction *)), this, SLOT(OnAcceptAllPopupActivated(QAction *)));
orientationPopup.exec(QCursor::pos());
}
void QmitkSlicesInterpolator::OnAccept3DInterpolationClicked()
{
auto referenceImage = GetData<mitk::Image>(m_ToolManager->GetReferenceData(0));
auto* segmentationDataNode = m_ToolManager->GetWorkingData(0);
auto segmentation = GetData<mitk::Image>(segmentationDataNode);
if (referenceImage.IsNull() || segmentation.IsNull())
return;
const auto* segmentationGeometry = segmentation->GetTimeGeometry();
const auto timePoint = m_LastSNC->GetSelectedTimePoint();
if (!referenceImage->GetTimeGeometry()->IsValidTimePoint(timePoint) ||
!segmentationGeometry->IsValidTimePoint(timePoint))
{
MITK_WARN << "Cannot accept interpolation. Current time point is not within the time bounds of the patient image and segmentation.";
return;
}
auto interpolatedSurface = GetData<mitk::Surface>(m_InterpolatedSurfaceNode);
if (interpolatedSurface.IsNull())
return;
auto surfaceToImageFilter = mitk::SurfaceToImageFilter::New();
surfaceToImageFilter->SetImage(referenceImage);
surfaceToImageFilter->SetMakeOutputBinary(true);
- surfaceToImageFilter->SetUShortBinaryPixelType(itk::ImageIOBase::USHORT == segmentation->GetPixelType().GetComponentType());
+ surfaceToImageFilter->SetUShortBinaryPixelType(itk::IOComponentEnum::USHORT == segmentation->GetPixelType().GetComponentType());
surfaceToImageFilter->SetInput(interpolatedSurface);
surfaceToImageFilter->Update();
mitk::Image::Pointer interpolatedSegmentation = surfaceToImageFilter->GetOutput();
auto timeStep = interpolatedSegmentation->GetTimeGeometry()->TimePointToTimeStep(timePoint);
mitk::ImageReadAccessor readAccessor(interpolatedSegmentation, interpolatedSegmentation->GetVolumeData(timeStep));
const auto* dataPointer = readAccessor.GetData();
if (nullptr == dataPointer)
return;
timeStep = segmentationGeometry->TimePointToTimeStep(timePoint);
segmentation->SetVolume(dataPointer, timeStep, 0);
m_CmbInterpolation->setCurrentIndex(0);
this->Show3DInterpolationResult(false);
std::string name = segmentationDataNode->GetName() + "_3D-interpolation";
mitk::TimeBounds timeBounds;
if (1 < interpolatedSurface->GetTimeSteps())
{
name += "_t" + std::to_string(timeStep);
auto* polyData = vtkPolyData::New();
polyData->DeepCopy(interpolatedSurface->GetVtkPolyData(timeStep));
auto surface = mitk::Surface::New();
surface->SetVtkPolyData(polyData);
interpolatedSurface = surface;
timeBounds = segmentationGeometry->GetTimeBounds(timeStep);
}
else
{
timeBounds = segmentationGeometry->GetTimeBounds(0);
}
auto* surfaceGeometry = static_cast<mitk::ProportionalTimeGeometry*>(interpolatedSurface->GetTimeGeometry());
surfaceGeometry->SetFirstTimePoint(timeBounds[0]);
surfaceGeometry->SetStepDuration(timeBounds[1] - timeBounds[0]);
// Typical file formats for surfaces do not save any time-related information. As a workaround at least for MITK scene files, we have the
// possibility to seralize this information as properties.
interpolatedSurface->SetProperty("ProportionalTimeGeometry.FirstTimePoint", mitk::FloatProperty::New(surfaceGeometry->GetFirstTimePoint()));
interpolatedSurface->SetProperty("ProportionalTimeGeometry.StepDuration", mitk::FloatProperty::New(surfaceGeometry->GetStepDuration()));
auto interpolatedSurfaceDataNode = mitk::DataNode::New();
interpolatedSurfaceDataNode->SetData(interpolatedSurface);
interpolatedSurfaceDataNode->SetName(name);
interpolatedSurfaceDataNode->SetOpacity(0.7f);
std::array<float, 3> rgb;
segmentationDataNode->GetColor(rgb.data());
interpolatedSurfaceDataNode->SetColor(rgb.data());
m_DataStorage->Add(interpolatedSurfaceDataNode, segmentationDataNode);
}
void ::QmitkSlicesInterpolator::OnSuggestPlaneClicked()
{
if (m_PlaneWatcher.isRunning())
m_PlaneWatcher.waitForFinished();
m_PlaneFuture = QtConcurrent::run(this, &QmitkSlicesInterpolator::RunPlaneSuggestion);
m_PlaneWatcher.setFuture(m_PlaneFuture);
}
void ::QmitkSlicesInterpolator::RunPlaneSuggestion()
{
if (m_FirstRun)
mitk::ProgressBar::GetInstance()->AddStepsToDo(7);
else
mitk::ProgressBar::GetInstance()->AddStepsToDo(3);
m_EdgeDetector->SetSegmentationMask(m_Segmentation);
m_EdgeDetector->SetInput(dynamic_cast<mitk::Image *>(m_ToolManager->GetReferenceData(0)->GetData()));
m_EdgeDetector->Update();
mitk::UnstructuredGrid::Pointer uGrid = mitk::UnstructuredGrid::New();
uGrid->SetVtkUnstructuredGrid(m_EdgeDetector->GetOutput()->GetVtkUnstructuredGrid());
mitk::ProgressBar::GetInstance()->Progress();
mitk::Surface::Pointer surface = dynamic_cast<mitk::Surface *>(m_InterpolatedSurfaceNode->GetData());
vtkSmartPointer<vtkPolyData> vtkpoly = surface->GetVtkPolyData();
vtkSmartPointer<vtkPoints> vtkpoints = vtkpoly->GetPoints();
vtkSmartPointer<vtkUnstructuredGrid> vGrid = vtkSmartPointer<vtkUnstructuredGrid>::New();
vtkSmartPointer<vtkPolyVertex> verts = vtkSmartPointer<vtkPolyVertex>::New();
verts->GetPointIds()->SetNumberOfIds(vtkpoints->GetNumberOfPoints());
for (int i = 0; i < vtkpoints->GetNumberOfPoints(); i++)
{
verts->GetPointIds()->SetId(i, i);
}
vGrid->Allocate(1);
vGrid->InsertNextCell(verts->GetCellType(), verts->GetPointIds());
vGrid->SetPoints(vtkpoints);
mitk::UnstructuredGrid::Pointer interpolationGrid = mitk::UnstructuredGrid::New();
interpolationGrid->SetVtkUnstructuredGrid(vGrid);
m_PointScorer->SetInput(0, uGrid);
m_PointScorer->SetInput(1, interpolationGrid);
m_PointScorer->Update();
mitk::UnstructuredGrid::Pointer scoredGrid = mitk::UnstructuredGrid::New();
scoredGrid = m_PointScorer->GetOutput();
mitk::ProgressBar::GetInstance()->Progress();
double spacing = mitk::SurfaceInterpolationController::GetInstance()->GetDistanceImageSpacing();
mitk::UnstructuredGridClusteringFilter::Pointer clusterFilter = mitk::UnstructuredGridClusteringFilter::New();
clusterFilter->SetInput(scoredGrid);
clusterFilter->SetMeshing(false);
clusterFilter->SetMinPts(4);
clusterFilter->Seteps(spacing);
clusterFilter->Update();
mitk::ProgressBar::GetInstance()->Progress();
// Create plane suggestion
mitk::BaseRenderer::Pointer br =
mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget0"));
mitk::PlaneProposer planeProposer;
std::vector<mitk::UnstructuredGrid::Pointer> grids = clusterFilter->GetAllClusters();
planeProposer.SetUnstructuredGrids(grids);
mitk::SliceNavigationController::Pointer snc = br->GetSliceNavigationController();
planeProposer.SetSliceNavigationController(snc);
planeProposer.SetUseDistances(true);
try
{
planeProposer.CreatePlaneInfo();
}
catch (const mitk::Exception &e)
{
MITK_ERROR << e.what();
}
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
m_FirstRun = false;
}
void QmitkSlicesInterpolator::OnReinit3DInterpolation()
{
mitk::NodePredicateProperty::Pointer pred =
mitk::NodePredicateProperty::New("3DContourContainer", mitk::BoolProperty::New(true));
mitk::DataStorage::SetOfObjects::ConstPointer contourNodes =
m_DataStorage->GetDerivations(m_ToolManager->GetWorkingData(0), pred);
if (contourNodes->Size() != 0)
{
m_BtnApply3D->setEnabled(true);
m_3DContourNode = contourNodes->at(0);
mitk::Surface::Pointer contours = dynamic_cast<mitk::Surface *>(m_3DContourNode->GetData());
if (contours)
mitk::SurfaceInterpolationController::GetInstance()->ReinitializeInterpolation(contours);
m_BtnReinit3DInterpolation->setEnabled(false);
}
else
{
m_BtnApply3D->setEnabled(false);
QMessageBox errorInfo;
errorInfo.setWindowTitle("Reinitialize surface interpolation");
errorInfo.setIcon(QMessageBox::Information);
errorInfo.setText("No contours available for the selected segmentation!");
errorInfo.exec();
}
}
void QmitkSlicesInterpolator::OnAcceptAllPopupActivated(QAction *action)
{
try
{
std::map<QAction *, mitk::SliceNavigationController *>::const_iterator iter = ACTION_TO_SLICEDIMENSION.find(action);
if (iter != ACTION_TO_SLICEDIMENSION.end())
{
mitk::SliceNavigationController *slicer = iter->second;
AcceptAllInterpolations(slicer);
}
}
catch (...)
{
/* Showing message box with possible memory error */
QMessageBox errorInfo;
errorInfo.setWindowTitle("Interpolation Process");
errorInfo.setIcon(QMessageBox::Critical);
errorInfo.setText("An error occurred during interpolation. Possible cause: Not enough memory!");
errorInfo.exec();
// additional error message on std::cerr
std::cerr << "Ill construction in " __FILE__ " l. " << __LINE__ << std::endl;
}
}
void QmitkSlicesInterpolator::OnInterpolationActivated(bool on)
{
m_2DInterpolationEnabled = on;
try
{
if (m_DataStorage.IsNotNull())
{
if (on && !m_DataStorage->Exists(m_FeedbackNode))
{
m_DataStorage->Add(m_FeedbackNode);
}
}
}
catch (...)
{
// don't care (double add/remove)
}
if (m_ToolManager)
{
mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0);
mitk::DataNode *referenceNode = m_ToolManager->GetReferenceData(0);
QWidget::setEnabled(workingNode != nullptr);
m_BtnApply2D->setEnabled(on);
m_FeedbackNode->SetVisibility(on);
if (!on)
{
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
return;
}
if (workingNode)
{
mitk::Image *segmentation = dynamic_cast<mitk::Image *>(workingNode->GetData());
if (segmentation)
{
m_Interpolator->SetSegmentationVolume(segmentation);
if (referenceNode)
{
mitk::Image *referenceImage = dynamic_cast<mitk::Image *>(referenceNode->GetData());
m_Interpolator->SetReferenceVolume(referenceImage); // may be nullptr
}
}
}
}
UpdateVisibleSuggestion();
}
void QmitkSlicesInterpolator::Run3DInterpolation()
{
m_SurfaceInterpolator->Interpolate();
}
void QmitkSlicesInterpolator::StartUpdateInterpolationTimer()
{
m_Timer->start(500);
}
void QmitkSlicesInterpolator::StopUpdateInterpolationTimer()
{
m_Timer->stop();
m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(SURFACE_COLOR_RGB));
mitk::RenderingManager::GetInstance()->RequestUpdate(
mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3"))->GetRenderWindow());
}
void QmitkSlicesInterpolator::ChangeSurfaceColor()
{
float currentColor[3];
m_InterpolatedSurfaceNode->GetColor(currentColor);
if (currentColor[2] == SURFACE_COLOR_RGB[2])
{
m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(1.0f, 1.0f, 1.0f));
}
else
{
m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(SURFACE_COLOR_RGB));
}
m_InterpolatedSurfaceNode->Update();
mitk::RenderingManager::GetInstance()->RequestUpdate(
mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3"))->GetRenderWindow());
}
void QmitkSlicesInterpolator::On3DInterpolationActivated(bool on)
{
m_3DInterpolationEnabled = on;
this->CheckSupportedImageDimension();
try
{
if (m_DataStorage.IsNotNull() && m_ToolManager && m_3DInterpolationEnabled)
{
mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0);
if (workingNode)
{
if ((workingNode->IsVisible(mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget2")))))
{
int ret = QMessageBox::Yes;
if (m_SurfaceInterpolator->EstimatePortionOfNeededMemory() > 0.5)
{
QMessageBox msgBox;
msgBox.setText("Due to short handed system memory the 3D interpolation may be very slow!");
msgBox.setInformativeText("Are you sure you want to activate the 3D interpolation?");
msgBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes);
ret = msgBox.exec();
}
if (m_Watcher.isRunning())
m_Watcher.waitForFinished();
if (ret == QMessageBox::Yes)
{
m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation);
m_Watcher.setFuture(m_Future);
}
else
{
m_CmbInterpolation->setCurrentIndex(0);
}
}
}
else
{
QWidget::setEnabled(false);
m_ChkShowPositionNodes->setEnabled(m_3DInterpolationEnabled);
}
}
if (!m_3DInterpolationEnabled)
{
this->Show3DInterpolationResult(false);
m_BtnApply3D->setEnabled(m_3DInterpolationEnabled);
// T28261
// m_BtnSuggestPlane->setEnabled(m_3DInterpolationEnabled);
}
}
catch (...)
{
MITK_ERROR << "Error with 3D surface interpolation!";
}
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkSlicesInterpolator::EnableInterpolation(bool on)
{
// only to be called from the outside world
// just a redirection to OnInterpolationActivated
OnInterpolationActivated(on);
}
void QmitkSlicesInterpolator::Enable3DInterpolation(bool on)
{
// only to be called from the outside world
// just a redirection to OnInterpolationActivated
On3DInterpolationActivated(on);
}
void QmitkSlicesInterpolator::UpdateVisibleSuggestion()
{
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkSlicesInterpolator::OnInterpolationInfoChanged(const itk::EventObject & /*e*/)
{
// something (e.g. undo) changed the interpolation info, we should refresh our display
UpdateVisibleSuggestion();
}
void QmitkSlicesInterpolator::OnInterpolationAborted(const itk::EventObject& /*e*/)
{
m_CmbInterpolation->setCurrentIndex(0);
m_FeedbackNode->SetData(nullptr);
}
void QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged(const itk::EventObject & /*e*/)
{
if (m_3DInterpolationEnabled)
{
if (m_Watcher.isRunning())
m_Watcher.waitForFinished();
m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation);
m_Watcher.setFuture(m_Future);
}
}
void QmitkSlicesInterpolator::SetCurrentContourListID()
{
// New ContourList = hide current interpolation
Show3DInterpolationResult(false);
if (m_DataStorage.IsNotNull() && m_ToolManager && m_LastSNC)
{
mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0);
if (workingNode)
{
QWidget::setEnabled(true);
const auto timePoint = m_LastSNC->GetSelectedTimePoint();
// In case the time is not valid use 0 to access the time geometry of the working node
unsigned int time_position = 0;
if (!workingNode->GetData()->GetTimeGeometry()->IsValidTimePoint(timePoint))
{
MITK_WARN << "Cannot accept interpolation. Time point selected by SliceNavigationController is not within the time bounds of WorkingImage. Time point: " << timePoint;
return;
}
time_position = workingNode->GetData()->GetTimeGeometry()->TimePointToTimeStep(timePoint);
mitk::Vector3D spacing = workingNode->GetData()->GetGeometry(time_position)->GetSpacing();
double minSpacing(100);
double maxSpacing(0);
for (int i = 0; i < 3; i++)
{
if (spacing[i] < minSpacing)
{
minSpacing = spacing[i];
}
if (spacing[i] > maxSpacing)
{
maxSpacing = spacing[i];
}
}
m_SurfaceInterpolator->SetMaxSpacing(maxSpacing);
m_SurfaceInterpolator->SetMinSpacing(minSpacing);
m_SurfaceInterpolator->SetDistanceImageVolume(50000);
mitk::Image *segmentationImage = dynamic_cast<mitk::Image *>(workingNode->GetData());
m_SurfaceInterpolator->SetCurrentInterpolationSession(segmentationImage);
m_SurfaceInterpolator->SetCurrentTimePoint(timePoint);
if (m_3DInterpolationEnabled)
{
if (m_Watcher.isRunning())
m_Watcher.waitForFinished();
m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation);
m_Watcher.setFuture(m_Future);
}
}
else
{
QWidget::setEnabled(false);
}
}
}
void QmitkSlicesInterpolator::Show3DInterpolationResult(bool status)
{
if (m_InterpolatedSurfaceNode.IsNotNull())
m_InterpolatedSurfaceNode->SetVisibility(status);
if (m_3DContourNode.IsNotNull())
m_3DContourNode->SetVisibility(
status, mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3")));
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkSlicesInterpolator::CheckSupportedImageDimension()
{
if (m_ToolManager->GetWorkingData(0))
m_Segmentation = dynamic_cast<mitk::Image *>(m_ToolManager->GetWorkingData(0)->GetData());
/*if (m_3DInterpolationEnabled && m_Segmentation && m_Segmentation->GetDimension() != 3)
{
QMessageBox info;
info.setWindowTitle("3D Interpolation Process");
info.setIcon(QMessageBox::Information);
info.setText("3D Interpolation is only supported for 3D images at the moment!");
info.exec();
m_CmbInterpolation->setCurrentIndex(0);
}*/
}
void QmitkSlicesInterpolator::OnSliceNavigationControllerDeleted(const itk::Object *sender,
const itk::EventObject & /*e*/)
{
// Don't know how to avoid const_cast here?!
mitk::SliceNavigationController *slicer =
dynamic_cast<mitk::SliceNavigationController *>(const_cast<itk::Object *>(sender));
if (slicer)
{
m_ControllerToTimeObserverTag.remove(slicer);
m_ControllerToSliceObserverTag.remove(slicer);
m_ControllerToDeleteObserverTag.remove(slicer);
}
}
void QmitkSlicesInterpolator::WaitForFutures()
{
if (m_Watcher.isRunning())
{
m_Watcher.waitForFinished();
}
if (m_PlaneWatcher.isRunning())
{
m_PlaneWatcher.waitForFinished();
}
}
void QmitkSlicesInterpolator::NodeRemoved(const mitk::DataNode* node)
{
if ((m_ToolManager && m_ToolManager->GetWorkingData(0) == node) ||
node == m_3DContourNode ||
node == m_FeedbackNode ||
node == m_InterpolatedSurfaceNode)
{
WaitForFutures();
}
}
diff --git a/Modules/SegmentationUI/resources/NewLabel_48x48.png b/Modules/SegmentationUI/resources/NewLabel_48x48.png
index c11110bda6..defcc15ebc 100644
Binary files a/Modules/SegmentationUI/resources/NewLabel_48x48.png and b/Modules/SegmentationUI/resources/NewLabel_48x48.png differ
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/NewSegmentationSession_48x48.png b/Modules/SegmentationUI/resources/NewSegmentation_48x48.png
similarity index 100%
rename from Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/NewSegmentationSession_48x48.png
rename to Modules/SegmentationUI/resources/NewSegmentation_48x48.png
diff --git a/Modules/SegmentationUI/resources/SegmentationUI.qrc b/Modules/SegmentationUI/resources/SegmentationUI.qrc
index d39c0b1016..cfab8d398d 100644
--- a/Modules/SegmentationUI/resources/SegmentationUI.qrc
+++ b/Modules/SegmentationUI/resources/SegmentationUI.qrc
@@ -1,39 +1,40 @@
<RCC>
<qresource prefix="/Qmitk" >
<file>AcceptCurrentInterpolation.png</file>
<file>AcceptAllInterpolations.png</file>
<file>AcceptSurfaceInterpolation.png</file>
<file>BooleanDifference_48x48.png</file>
<file>BooleanIntersection_48x48.png</file>
<file>BooleanUnion_48x48.png</file>
<file>Accept_48x48.png</file>
<file>Cancel_48x48.png</file>
<file>Run_48x48.png</file>
<file>ImportLabelSet_48x48.png</file>
<file>ImportLabeledImage_48x48.png</file>
<file>DeleteLayer_48x48.png</file>
<file>PreviousLayer_48x48.png</file>
<file>NextLayer_48x48.png</file>
<file>AddLayer_48x48.png</file>
<file>SegmentationInteractor_48x48.png</file>
<file>LockExterior_48x48.png</file>
<file>UnlockExterior_48x48.png</file>
<file>NewLabel_48x48.png</file>
+ <file>NewSegmentation_48x48.png</file>
<file>ClearSeeds_48x48.png</file>
<file>Start.png</file>
<file>Stop.png</file>
<file>Help_48x48.png</file>
<file>AdvancedTools.png</file>
<file>visible.svg</file>
<file>invisible.svg</file>
<file>lock.svg</file>
<file>unlock.svg</file>
<file>MergeLabels.png</file>
<file>RemoveLabel.png</file>
<file>EraseLabel.png</file>
<file>CreateSurface.png</file>
<file>CreateMask.png</file>
<file>RandomColor.png</file>
<file>RenameLabel.png</file>
</qresource>
</RCC>
diff --git a/Modules/SurfaceInterpolation/mitkComputeContourSetNormalsFilter.cpp b/Modules/SurfaceInterpolation/mitkComputeContourSetNormalsFilter.cpp
index 7589742cdf..5e1f5e62b5 100644
--- a/Modules/SurfaceInterpolation/mitkComputeContourSetNormalsFilter.cpp
+++ b/Modules/SurfaceInterpolation/mitkComputeContourSetNormalsFilter.cpp
@@ -1,343 +1,343 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkComputeContourSetNormalsFilter.h"
#include "mitkIOUtil.h"
#include "mitkImagePixelReadAccessor.h"
mitk::ComputeContourSetNormalsFilter::ComputeContourSetNormalsFilter()
: m_SegmentationBinaryImage(nullptr),
m_MaxSpacing(5),
m_NegativeNormalCounter(0),
m_PositiveNormalCounter(0),
m_UseProgressBar(false),
m_ProgressStepSize(1)
{
mitk::Surface::Pointer output = mitk::Surface::New();
this->SetNthOutput(0, output.GetPointer());
}
mitk::ComputeContourSetNormalsFilter::~ComputeContourSetNormalsFilter()
{
}
void mitk::ComputeContourSetNormalsFilter::GenerateData()
{
unsigned int numberOfInputs = this->GetNumberOfIndexedInputs();
// Iterating over each input
for (unsigned int i = 0; i < numberOfInputs; i++)
{
// Getting the inputs polydata and polygons
auto *currentSurface = this->GetInput(i);
vtkPolyData *polyData = currentSurface->GetVtkPolyData();
vtkSmartPointer<vtkCellArray> existingPolys = polyData->GetPolys();
vtkSmartPointer<vtkPoints> existingPoints = polyData->GetPoints();
existingPolys->InitTraversal();
const vtkIdType *cell(nullptr);
vtkIdType cellSize(0);
// The array that contains all the vertex normals of the current polygon
vtkSmartPointer<vtkDoubleArray> normals = vtkSmartPointer<vtkDoubleArray>::New();
normals->SetNumberOfComponents(3);
normals->SetNumberOfTuples(polyData->GetNumberOfPoints());
// If the current contour is an inner contour then the direction is -1
// A contour lies inside another one if the pixel values in the direction of the normal is 1
m_NegativeNormalCounter = 0;
m_PositiveNormalCounter = 0;
vtkIdType offSet(0);
// Iterating over each polygon
for (existingPolys->InitTraversal(); existingPolys->GetNextCell(cellSize, cell);)
{
if (cellSize < 3)
continue;
// First we calculate the current polygon's normal
double polygonNormal[3] = {0.0};
double p1[3];
double p2[3];
double v1[3];
double v2[3];
existingPoints->GetPoint(cell[0], p1);
unsigned int index = cellSize * 0.5;
existingPoints->GetPoint(cell[index], p2);
v1[0] = p2[0] - p1[0];
v1[1] = p2[1] - p1[1];
v1[2] = p2[2] - p1[2];
for (vtkIdType k = 2; k < cellSize; k++)
{
index = cellSize * 0.25;
existingPoints->GetPoint(cell[index], p1);
index = cellSize * 0.75;
existingPoints->GetPoint(cell[index], p2);
v2[0] = p2[0] - p1[0];
v2[1] = p2[1] - p1[1];
v2[2] = p2[2] - p1[2];
vtkMath::Cross(v1, v2, polygonNormal);
if (vtkMath::Norm(polygonNormal) != 0)
break;
}
vtkMath::Normalize(polygonNormal);
// Now we start computing the normal for each vertex
double vertexNormalTemp[3];
existingPoints->GetPoint(cell[0], p1);
existingPoints->GetPoint(cell[1], p2);
v1[0] = p2[0] - p1[0];
v1[1] = p2[1] - p1[1];
v1[2] = p2[2] - p1[2];
vtkMath::Cross(v1, polygonNormal, vertexNormalTemp);
vtkMath::Normalize(vertexNormalTemp);
double vertexNormal[3];
for (vtkIdType j = 0; j < cellSize - 2; j++)
{
existingPoints->GetPoint(cell[j + 1], p1);
existingPoints->GetPoint(cell[j + 2], p2);
v1[0] = p2[0] - p1[0];
v1[1] = p2[1] - p1[1];
v1[2] = p2[2] - p1[2];
vtkMath::Cross(v1, polygonNormal, vertexNormal);
vtkMath::Normalize(vertexNormal);
double finalNormal[3];
finalNormal[0] = (vertexNormal[0] + vertexNormalTemp[0]) * 0.5;
finalNormal[1] = (vertexNormal[1] + vertexNormalTemp[1]) * 0.5;
finalNormal[2] = (vertexNormal[2] + vertexNormalTemp[2]) * 0.5;
vtkMath::Normalize(finalNormal);
// Here we determine the direction of the normal
if (m_SegmentationBinaryImage)
{
Point3D worldCoord;
worldCoord[0] = p1[0] + finalNormal[0] * m_MaxSpacing;
worldCoord[1] = p1[1] + finalNormal[1] * m_MaxSpacing;
worldCoord[2] = p1[2] + finalNormal[2] * m_MaxSpacing;
double val = 0.0;
itk::Index<3> idx;
m_SegmentationBinaryImage->GetGeometry()->WorldToIndex(worldCoord, idx);
try
{
if (m_SegmentationBinaryImage->GetImageDescriptor()
->GetChannelDescriptor()
.GetPixelType()
- .GetComponentType() == itk::ImageIOBase::UCHAR)
+ .GetComponentType() == itk::IOComponentEnum::UCHAR)
{
mitk::ImagePixelReadAccessor<unsigned char> readAccess(m_SegmentationBinaryImage);
val = readAccess.GetPixelByIndexSafe(idx);
}
else if (m_SegmentationBinaryImage->GetImageDescriptor()
->GetChannelDescriptor()
.GetPixelType()
- .GetComponentType() == itk::ImageIOBase::USHORT)
+ .GetComponentType() == itk::IOComponentEnum::USHORT)
{
mitk::ImagePixelReadAccessor<unsigned short> readAccess(m_SegmentationBinaryImage);
val = readAccess.GetPixelByIndexSafe(idx);
}
}
catch (const mitk::Exception &e)
{
// If value is outside the image's region ignore it
MITK_WARN << e.what();
}
if (val == 0.0)
{
// MITK_INFO << "val equals zero.";
++m_PositiveNormalCounter;
}
else
{
// MITK_INFO << "val does not equal zero.";
++m_NegativeNormalCounter;
}
}
vertexNormalTemp[0] = vertexNormal[0];
vertexNormalTemp[1] = vertexNormal[1];
vertexNormalTemp[2] = vertexNormal[2];
vtkIdType id = cell[j + 1];
normals->SetTuple(id, finalNormal);
}
existingPoints->GetPoint(cell[0], p1);
existingPoints->GetPoint(cell[1], p2);
v1[0] = p2[0] - p1[0];
v1[1] = p2[1] - p1[1];
v1[2] = p2[2] - p1[2];
vtkMath::Cross(v1, polygonNormal, vertexNormal);
vtkMath::Normalize(vertexNormal);
vertexNormal[0] = (vertexNormal[0] + vertexNormalTemp[0]) * 0.5;
vertexNormal[1] = (vertexNormal[1] + vertexNormalTemp[1]) * 0.5;
vertexNormal[2] = (vertexNormal[2] + vertexNormalTemp[2]) * 0.5;
vtkMath::Normalize(vertexNormal);
vtkIdType id = cell[0];
normals->SetTuple(id, vertexNormal);
id = cell[cellSize - 1];
normals->SetTuple(id, vertexNormal);
if (m_NegativeNormalCounter > m_PositiveNormalCounter)
{
for (vtkIdType n = 0; n < cellSize; n++)
{
double normal[3];
normals->GetTuple(offSet + n, normal);
normal[0] = (-1) * normal[0];
normal[1] = (-1) * normal[1];
normal[2] = (-1) * normal[2];
normals->SetTuple(offSet + n, normal);
}
}
m_NegativeNormalCounter = 0;
m_PositiveNormalCounter = 0;
offSet += cellSize;
} // end for all cells
Surface::Pointer surface = this->GetOutput(i);
surface->GetVtkPolyData()->GetCellData()->SetNormals(normals);
} // end for all inputs
// Setting progressbar
if (this->m_UseProgressBar)
mitk::ProgressBar::GetInstance()->Progress(this->m_ProgressStepSize);
}
mitk::Surface::Pointer mitk::ComputeContourSetNormalsFilter::GetNormalsAsSurface()
{
// Just for debugging:
vtkSmartPointer<vtkPolyData> newPolyData = vtkSmartPointer<vtkPolyData>::New();
vtkSmartPointer<vtkCellArray> newLines = vtkSmartPointer<vtkCellArray>::New();
vtkSmartPointer<vtkPoints> newPoints = vtkSmartPointer<vtkPoints>::New();
unsigned int idCounter(0);
// Debug end
for (unsigned int i = 0; i < this->GetNumberOfIndexedOutputs(); i++)
{
auto *currentSurface = this->GetOutput(i);
vtkPolyData *polyData = currentSurface->GetVtkPolyData();
vtkSmartPointer<vtkDoubleArray> currentCellNormals =
vtkDoubleArray::SafeDownCast(polyData->GetCellData()->GetNormals());
vtkSmartPointer<vtkCellArray> existingPolys = polyData->GetPolys();
vtkSmartPointer<vtkPoints> existingPoints = polyData->GetPoints();
existingPolys->InitTraversal();
const vtkIdType *cell(nullptr);
vtkIdType cellSize(0);
for (existingPolys->InitTraversal(); existingPolys->GetNextCell(cellSize, cell);)
{
for (vtkIdType j = 0; j < cellSize; j++)
{
double currentNormal[3];
currentCellNormals->GetTuple(cell[j], currentNormal);
vtkSmartPointer<vtkLine> line = vtkSmartPointer<vtkLine>::New();
line->GetPointIds()->SetNumberOfIds(2);
double newPoint[3];
double p0[3];
existingPoints->GetPoint(cell[j], p0);
newPoint[0] = p0[0] + currentNormal[0];
newPoint[1] = p0[1] + currentNormal[1];
newPoint[2] = p0[2] + currentNormal[2];
line->GetPointIds()->SetId(0, idCounter);
newPoints->InsertPoint(idCounter, p0);
idCounter++;
line->GetPointIds()->SetId(1, idCounter);
newPoints->InsertPoint(idCounter, newPoint);
idCounter++;
newLines->InsertNextCell(line);
} // end for all points
} // end for all cells
} // end for all outputs
newPolyData->SetPoints(newPoints);
newPolyData->SetLines(newLines);
newPolyData->BuildCells();
mitk::Surface::Pointer surface = mitk::Surface::New();
surface->SetVtkPolyData(newPolyData);
return surface;
}
void mitk::ComputeContourSetNormalsFilter::SetMaxSpacing(double maxSpacing)
{
m_MaxSpacing = maxSpacing;
}
void mitk::ComputeContourSetNormalsFilter::GenerateOutputInformation()
{
Superclass::GenerateOutputInformation();
}
void mitk::ComputeContourSetNormalsFilter::Reset()
{
for (unsigned int i = 0; i < this->GetNumberOfIndexedInputs(); i++)
{
this->PopBackInput();
}
this->SetNumberOfIndexedInputs(0);
this->SetNumberOfIndexedOutputs(0);
mitk::Surface::Pointer output = mitk::Surface::New();
this->SetNthOutput(0, output.GetPointer());
}
void mitk::ComputeContourSetNormalsFilter::SetUseProgressBar(bool status)
{
this->m_UseProgressBar = status;
}
void mitk::ComputeContourSetNormalsFilter::SetProgressStepSize(unsigned int stepSize)
{
this->m_ProgressStepSize = stepSize;
}
diff --git a/Modules/ToFHardware/mitkToFCameraDevice.cpp b/Modules/ToFHardware/mitkToFCameraDevice.cpp
index 780f44b8f0..8e07f705a7 100644
--- a/Modules/ToFHardware/mitkToFCameraDevice.cpp
+++ b/Modules/ToFHardware/mitkToFCameraDevice.cpp
@@ -1,185 +1,179 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkToFCameraDevice.h"
#include <itksys/SystemTools.hxx>
namespace mitk
{
ToFCameraDevice::ToFCameraDevice():m_BufferSize(1),m_MaxBufferSize(100),m_CurrentPos(-1),m_FreePos(0),
m_CaptureWidth(204),m_CaptureHeight(204),m_PixelNumber(41616),m_SourceDataSize(0),
- m_ThreadID(0),m_CameraActive(false),m_CameraConnected(false),m_ImageSequence(0)
+ m_CameraActive(false),m_CameraConnected(false),m_ImageSequence(0)
{
this->m_AmplitudeArray = nullptr;
this->m_IntensityArray = nullptr;
this->m_DistanceArray = nullptr;
this->m_PropertyList = mitk::PropertyList::New();
//By default, all devices have no further images (just a distance image)
//If a device provides more data (e.g. RGB, Intensity, Amplitde images,
//the property has to be true.
this->m_PropertyList->SetBoolProperty("HasRGBImage", false);
this->m_PropertyList->SetBoolProperty("HasIntensityImage", false);
this->m_PropertyList->SetBoolProperty("HasAmplitudeImage", false);
- this->m_MultiThreader = itk::MultiThreader::New();
- this->m_ImageMutex = itk::FastMutexLock::New();
- this->m_CameraActiveMutex = itk::FastMutexLock::New();
-
this->m_RGBImageWidth = this->m_CaptureWidth;
this->m_RGBImageHeight = this->m_CaptureHeight;
this->m_RGBPixelNumber = this->m_RGBImageWidth* this->m_RGBImageHeight;
}
ToFCameraDevice::~ToFCameraDevice()
{
}
void ToFCameraDevice::SetBoolProperty( const char* propertyKey, bool boolValue )
{
SetProperty(propertyKey, mitk::BoolProperty::New(boolValue));
}
void ToFCameraDevice::SetIntProperty( const char* propertyKey, int intValue )
{
SetProperty(propertyKey, mitk::IntProperty::New(intValue));
}
void ToFCameraDevice::SetFloatProperty( const char* propertyKey, float floatValue )
{
SetProperty(propertyKey, mitk::FloatProperty::New(floatValue));
}
void ToFCameraDevice::SetStringProperty( const char* propertyKey, const char* stringValue )
{
SetProperty(propertyKey, mitk::StringProperty::New(stringValue));
}
void ToFCameraDevice::SetProperty( const char *propertyKey, BaseProperty* propertyValue )
{
this->m_PropertyList->SetProperty(propertyKey, propertyValue);
}
BaseProperty* ToFCameraDevice::GetProperty(const char *propertyKey)
{
return this->m_PropertyList->GetProperty(propertyKey);
}
bool ToFCameraDevice::GetBoolProperty(const char *propertyKey, bool& boolValue)
{
mitk::BoolProperty::Pointer boolprop = dynamic_cast<mitk::BoolProperty*>(this->GetProperty(propertyKey));
if(boolprop.IsNull())
return false;
boolValue = boolprop->GetValue();
return true;
}
bool ToFCameraDevice::GetStringProperty(const char *propertyKey, std::string& string)
{
mitk::StringProperty::Pointer stringProp = dynamic_cast<mitk::StringProperty*>(this->GetProperty(propertyKey));
if(stringProp.IsNull())
{
return false;
}
else
{
string = stringProp->GetValue();
return true;
}
}
bool ToFCameraDevice::GetIntProperty(const char *propertyKey, int& integer)
{
mitk::IntProperty::Pointer intProp = dynamic_cast<mitk::IntProperty*>(this->GetProperty(propertyKey));
if(intProp.IsNull())
{
return false;
}
else
{
integer = intProp->GetValue();
return true;
}
}
void ToFCameraDevice::CleanupPixelArrays()
{
if (m_IntensityArray)
{
delete [] m_IntensityArray;
}
if (m_DistanceArray)
{
delete [] m_DistanceArray;
}
if (m_AmplitudeArray)
{
delete [] m_AmplitudeArray;
}
}
void ToFCameraDevice::AllocatePixelArrays()
{
// free memory if it was already allocated
CleanupPixelArrays();
// allocate buffer
this->m_IntensityArray = new float[this->m_PixelNumber];
for(int i=0; i<this->m_PixelNumber; i++) {this->m_IntensityArray[i]=0.0;}
this->m_DistanceArray = new float[this->m_PixelNumber];
for(int i=0; i<this->m_PixelNumber; i++) {this->m_DistanceArray[i]=0.0;}
this->m_AmplitudeArray = new float[this->m_PixelNumber];
for(int i=0; i<this->m_PixelNumber; i++) {this->m_AmplitudeArray[i]=0.0;}
}
int ToFCameraDevice::GetRGBCaptureWidth()
{
return this->m_RGBImageWidth;
}
int ToFCameraDevice::GetRGBCaptureHeight()
{
return this->m_RGBImageHeight;
}
void ToFCameraDevice::StopCamera()
{
- m_CameraActiveMutex->Lock();
+ m_CameraActiveMutex.lock();
m_CameraActive = false;
- m_CameraActiveMutex->Unlock();
+ m_CameraActiveMutex.unlock();
itksys::SystemTools::Delay(100);
- if (m_MultiThreader.IsNotNull())
- {
- m_MultiThreader->TerminateThread(m_ThreadID);
- }
+ if (m_Thread.joinable())
+ m_Thread.detach();
// wait a little to make sure that the thread is terminated
itksys::SystemTools::Delay(100);
}
bool ToFCameraDevice::IsCameraActive()
{
- m_CameraActiveMutex->Lock();
+ m_CameraActiveMutex.lock();
bool ok = m_CameraActive;
- m_CameraActiveMutex->Unlock();
+ m_CameraActiveMutex.unlock();
return ok;
}
bool ToFCameraDevice::ConnectCamera()
{
// Prepare connection, fail if this fails.
if (! this->OnConnectCamera()) return false;
return true;
}
bool ToFCameraDevice::IsCameraConnected()
{
return m_CameraConnected;
}
}
diff --git a/Modules/ToFHardware/mitkToFCameraDevice.h b/Modules/ToFHardware/mitkToFCameraDevice.h
index be4f4537ad..9ec63d213c 100644
--- a/Modules/ToFHardware/mitkToFCameraDevice.h
+++ b/Modules/ToFHardware/mitkToFCameraDevice.h
@@ -1,231 +1,231 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef __mitkToFCameraDevice_h
#define __mitkToFCameraDevice_h
#include <MitkToFHardwareExports.h>
#include "mitkCommon.h"
#include "mitkStringProperty.h"
#include "mitkProperties.h"
#include "mitkPropertyList.h"
#include "itkObject.h"
#include "itkObjectFactory.h"
-#include "itkMultiThreader.h"
-#include "itkFastMutexLock.h"
+
+#include <mutex>
+#include <thread>
// Microservices
#include <mitkServiceInterface.h>
namespace mitk
{
/**
* @brief Virtual interface and base class for all Time-of-Flight devices.
*
* @ingroup ToFHardware
*/
class MITKTOFHARDWARE_EXPORT ToFCameraDevice : public itk::Object
{
public:
mitkClassMacroItkParent(ToFCameraDevice, itk::Object);
/*!
\brief Opens a connection to the ToF camera. Has to be implemented
in the specialized inherited classes.
\return True for success.
*/
virtual bool OnConnectCamera() = 0;
/**
* @brief ConnectCamera Internally calls OnConnectCamera() of the
* respective device implementation.
* @return True for success.
*/
virtual bool ConnectCamera();
/*!
\brief closes the connection to the camera
*/
virtual bool DisconnectCamera() = 0;
/*!
\brief starts the continuous updating of the camera.
A separate thread updates the source data, the main thread processes the source data and creates images and coordinates
*/
virtual void StartCamera() = 0;
/*!
\brief stops the continuous updating of the camera
*/
virtual void StopCamera();
/*!
\brief returns true if the camera is connected and started
*/
virtual bool IsCameraActive();
/*!
\brief returns true if the camera is connected
*/
virtual bool IsCameraConnected();
/*!
\brief updates the camera for image acquisition
*/
virtual void UpdateCamera() = 0;
/*!
\brief gets the amplitude data from the ToF camera as the strength of the active illumination of every pixel
These values can be used to determine the quality of the distance values. The higher the amplitude value, the higher the accuracy of the according distance value
\param imageSequence the actually captured image sequence number
\param amplitudeArray contains the returned amplitude data as an array.
*/
virtual void GetAmplitudes(float* amplitudeArray, int& imageSequence) = 0;
/*!
\brief gets the intensity data from the ToF camera as a greyscale image
\param intensityArray contains the returned intensities data as an array.
\param imageSequence the actually captured image sequence number
*/
virtual void GetIntensities(float* intensityArray, int& imageSequence) = 0;
/*!
\brief gets the distance data from the ToF camera measuring the distance between the camera and the different object points in millimeters
\param distanceArray contains the returned distances data as an array.
\param imageSequence the actually captured image sequence number
*/
virtual void GetDistances(float* distanceArray, int& imageSequence) = 0;
/*!
\brief gets the 3 images (distance, amplitude, intensity) from the ToF camera. Caution! The user is responsible for allocating and deleting the images.
\param distanceArray contains the returned distance data as an array.
\param amplitudeArray contains the returned amplitude data as an array.
\param intensityArray contains the returned intensity data as an array.
\param sourceDataArray contains the complete source data from the camera device.
\param requiredImageSequence the required image sequence number
\param capturedImageSequence the actually captured image sequence number
\param rgbDataArray
*/
virtual void GetAllImages(float* distanceArray, float* amplitudeArray, float* intensityArray, char* sourceDataArray,
int requiredImageSequence, int& capturedImageSequence, unsigned char* rgbDataArray=nullptr) = 0;
/*!
\brief get the currently set capture width
\return capture width
*/
itkGetMacro(CaptureWidth, int);
/*!
\brief get the currently set capture height
\return capture height
*/
itkGetMacro(CaptureHeight, int);
/*!
\brief get the currently set source data size
\return source data size
*/
itkGetMacro(SourceDataSize, int);
/*!
\brief get the currently set buffer size
\return buffer size
*/
itkGetMacro(BufferSize, int);
/*!
\brief get the currently set max buffer size
\return max buffer size
*/
itkGetMacro(MaxBufferSize, int);
/*!
\brief set a bool property in the property list
*/
void SetBoolProperty( const char* propertyKey, bool boolValue );
/*!
\brief set an int property in the property list
*/
void SetIntProperty( const char* propertyKey, int intValue );
/*!
\brief set a float property in the property list
*/
void SetFloatProperty( const char* propertyKey, float floatValue );
/*!
\brief set a string property in the property list
*/
void SetStringProperty( const char* propertyKey, const char* stringValue );
/*!
\brief set a BaseProperty property in the property list
*/
virtual void SetProperty( const char *propertyKey, BaseProperty* propertyValue );
/*!
\brief get a BaseProperty from the property list
*/
virtual BaseProperty* GetProperty( const char *propertyKey );
/*!
\brief get a bool from the property list
*/
bool GetBoolProperty(const char *propertyKey, bool& boolValue);
/*!
\brief get a string from the property list
*/
bool GetStringProperty(const char *propertyKey, std::string& string);
/*!
\brief get an int from the property list
*/
bool GetIntProperty(const char *propertyKey, int& integer);
virtual int GetRGBCaptureWidth();
virtual int GetRGBCaptureHeight();
protected:
ToFCameraDevice();
~ToFCameraDevice() override;
/*!
\brief method for allocating memory for pixel arrays m_IntensityArray, m_DistanceArray and m_AmplitudeArray
*/
virtual void AllocatePixelArrays();
/*!
\brief method for cleanup memory allocated for pixel arrays m_IntensityArray, m_DistanceArray and m_AmplitudeArray
*/
virtual void CleanupPixelArrays();
float* m_IntensityArray; ///< float array holding the intensity image
float* m_DistanceArray; ///< float array holding the distance image
float* m_AmplitudeArray; ///< float array holding the amplitude image
int m_BufferSize; ///< buffer size of the image buffer needed for loss-less acquisition of range data
int m_MaxBufferSize; ///< maximal buffer size needed for initialization of data arrays. Default value is 100.
int m_CurrentPos; ///< current position in the buffer which will be retrieved by the Get methods
int m_FreePos; ///< current position in the buffer which will be filled with data acquired from the hardware
int m_CaptureWidth; ///< width of the range image (x dimension)
int m_CaptureHeight; ///< height of the range image (y dimension)
int m_PixelNumber; ///< number of pixels in the range image (m_CaptureWidth*m_CaptureHeight)
int m_RGBImageWidth; ///< width of the RGB image (x dimension)
int m_RGBImageHeight; ///< height of the RGB image (y dimension)
int m_RGBPixelNumber; ///< number of pixels in the range image (m_RGBImageWidth*m_RGBImageHeight)
int m_SourceDataSize; ///< size of the PMD source data
- itk::MultiThreader::Pointer m_MultiThreader; ///< itk::MultiThreader used for thread handling
- itk::FastMutexLock::Pointer m_ImageMutex; ///< mutex for images provided by the range camera
- itk::FastMutexLock::Pointer m_CameraActiveMutex; ///< mutex for the cameraActive flag
- int m_ThreadID; ///< ID of the started thread
+ std::mutex m_ImageMutex; ///< mutex for images provided by the range camera
+ std::mutex m_CameraActiveMutex; ///< mutex for the cameraActive flag
+ std::thread m_Thread;
bool m_CameraActive; ///< flag indicating if the camera is currently active or not. Caution: thread safe access only!
bool m_CameraConnected; ///< flag indicating if the camera is successfully connected or not. Caution: thread safe access only!
int m_ImageSequence; ///< counter for acquired images
PropertyList::Pointer m_PropertyList; ///< a list of the corresponding properties
};
} //END mitk namespace
/**
ToFCameraDevice is declared a MicroService interface. See
MicroService documenation for more details.
*/
MITK_DECLARE_SERVICE_INTERFACE(mitk::ToFCameraDevice, "org.mitk.services.ToFCameraDevice")
#endif
diff --git a/Modules/ToFHardware/mitkToFCameraMITKPlayerDevice.cpp b/Modules/ToFHardware/mitkToFCameraMITKPlayerDevice.cpp
index 1ea0743577..92372f5476 100644
--- a/Modules/ToFHardware/mitkToFCameraMITKPlayerDevice.cpp
+++ b/Modules/ToFHardware/mitkToFCameraMITKPlayerDevice.cpp
@@ -1,378 +1,362 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkToFCameraMITKPlayerDevice.h"
#include "mitkToFCameraMITKPlayerController.h"
#include "mitkRealTimeClock.h"
#include <iostream>
#include <fstream>
-#include <itkMultiThreader.h>
#include <itksys/SystemTools.hxx>
namespace mitk
{
ToFCameraMITKPlayerDevice::ToFCameraMITKPlayerDevice() :
m_DistanceDataBuffer(nullptr), m_AmplitudeDataBuffer(nullptr), m_IntensityDataBuffer(nullptr), m_RGBDataBuffer(nullptr)
{
m_Controller = ToFCameraMITKPlayerController::New();
}
ToFCameraMITKPlayerDevice::~ToFCameraMITKPlayerDevice()
{
DisconnectCamera();
CleanUpDataBuffers();
}
bool ToFCameraMITKPlayerDevice::OnConnectCamera()
{
bool ok = m_Controller->OpenCameraConnection();
if (ok)
{
this->m_CaptureWidth = m_Controller->GetCaptureWidth();
this->m_CaptureHeight = m_Controller->GetCaptureHeight();
this->m_RGBImageWidth = m_Controller->GetRGBCaptureWidth();
this->m_RGBImageHeight = m_Controller->GetRGBCaptureHeight();
this->m_PixelNumber = m_Controller->GetPixelNumber();
this->m_RGBPixelNumber = m_Controller->GetRGBPixelNumber();
if(m_RGBPixelNumber != m_PixelNumber)
this->SetBoolProperty("RGBImageHasDifferentResolution", true);
AllocatePixelArrays();
AllocateDataBuffers();
m_CameraConnected = true;
}
return ok;
}
bool ToFCameraMITKPlayerDevice::DisconnectCamera()
{
bool ok = m_Controller->CloseCameraConnection();
if (ok)
{
m_CameraConnected = false;
m_PropertyList->Clear();
}
return ok;
}
void ToFCameraMITKPlayerDevice::StartCamera()
{
if (m_CameraConnected)
{
// get the first image
this->m_Controller->UpdateCamera();
- this->m_ImageMutex->Lock();
+ this->m_ImageMutex.lock();
this->m_Controller->GetDistances(this->m_DistanceDataBuffer[this->m_FreePos]);
this->m_Controller->GetAmplitudes(this->m_AmplitudeDataBuffer[this->m_FreePos]);
this->m_Controller->GetIntensities(this->m_IntensityDataBuffer[this->m_FreePos]);
this->m_Controller->GetRgb(this->m_RGBDataBuffer[this->m_FreePos]);
this->m_FreePos = (this->m_FreePos+1) % this->m_BufferSize;
this->m_CurrentPos = (this->m_CurrentPos+1) % this->m_BufferSize;
this->m_ImageSequence++;
- this->m_ImageMutex->Unlock();
+ this->m_ImageMutex.unlock();
- this->m_CameraActiveMutex->Lock();
+ this->m_CameraActiveMutex.lock();
this->m_CameraActive = true;
- this->m_CameraActiveMutex->Unlock();
- this->m_ThreadID = this->m_MultiThreader->SpawnThread(this->Acquire, this);
+ this->m_CameraActiveMutex.unlock();
+ m_Thread = std::thread(&ToFCameraMITKPlayerDevice::Acquire, this);
// wait a little to make sure that the thread is started
itksys::SystemTools::Delay(10);
}
else
{
MITK_INFO<<"Camera not connected";
}
}
void ToFCameraMITKPlayerDevice::UpdateCamera()
{
m_Controller->UpdateCamera();
}
-ITK_THREAD_RETURN_TYPE ToFCameraMITKPlayerDevice::Acquire(void* pInfoStruct)
+void ToFCameraMITKPlayerDevice::Acquire()
{
- /* extract this pointer from Thread Info structure */
- struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct;
- if (pInfo == nullptr)
+ mitk::RealTimeClock::Pointer realTimeClock;
+ realTimeClock = mitk::RealTimeClock::New();
+ int n = 100;
+ double t1, t2;
+ t1 = realTimeClock->GetCurrentStamp();
+ bool overflow = false;
+ bool printStatus = false;
+ while (this->IsCameraActive())
{
- return ITK_THREAD_RETURN_VALUE;
- }
- if (pInfo->UserData == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
- ToFCameraMITKPlayerDevice* toFCameraDevice = (ToFCameraMITKPlayerDevice*)pInfo->UserData;
- if (toFCameraDevice!=nullptr)
- {
- mitk::RealTimeClock::Pointer realTimeClock;
- realTimeClock = mitk::RealTimeClock::New();
- int n = 100;
- double t1, t2;
- t1 = realTimeClock->GetCurrentStamp();
- bool overflow = false;
- bool printStatus = false;
- while (toFCameraDevice->IsCameraActive())
+ // update the ToF camera
+ this->UpdateCamera();
+ // get image data from controller and write it to the according buffer
+ m_Controller->GetDistances(m_DistanceDataBuffer[m_FreePos]);
+ m_Controller->GetAmplitudes(m_AmplitudeDataBuffer[m_FreePos]);
+ m_Controller->GetIntensities(m_IntensityDataBuffer[m_FreePos]);
+ m_Controller->GetRgb(m_RGBDataBuffer[m_FreePos]);
+ this->Modified();
+ m_ImageMutex.lock();
+ m_FreePos = (m_FreePos+1) % m_BufferSize;
+ m_CurrentPos = (m_CurrentPos+1) % m_BufferSize;
+ m_ImageSequence++;
+ if (m_FreePos == m_CurrentPos)
{
- // update the ToF camera
- toFCameraDevice->UpdateCamera();
- // get image data from controller and write it to the according buffer
- toFCameraDevice->m_Controller->GetDistances(toFCameraDevice->m_DistanceDataBuffer[toFCameraDevice->m_FreePos]);
- toFCameraDevice->m_Controller->GetAmplitudes(toFCameraDevice->m_AmplitudeDataBuffer[toFCameraDevice->m_FreePos]);
- toFCameraDevice->m_Controller->GetIntensities(toFCameraDevice->m_IntensityDataBuffer[toFCameraDevice->m_FreePos]);
- toFCameraDevice->m_Controller->GetRgb(toFCameraDevice->m_RGBDataBuffer[toFCameraDevice->m_FreePos]);
- toFCameraDevice->Modified();
- toFCameraDevice->m_ImageMutex->Lock();
- toFCameraDevice->m_FreePos = (toFCameraDevice->m_FreePos+1) % toFCameraDevice->m_BufferSize;
- toFCameraDevice->m_CurrentPos = (toFCameraDevice->m_CurrentPos+1) % toFCameraDevice->m_BufferSize;
- toFCameraDevice->m_ImageSequence++;
- if (toFCameraDevice->m_FreePos == toFCameraDevice->m_CurrentPos)
- {
- overflow = true;
- }
- if (toFCameraDevice->m_ImageSequence % n == 0)
- {
- printStatus = true;
- }
- toFCameraDevice->m_ImageMutex->Unlock();
- if (overflow)
- {
- overflow = false;
- }
- // print current framerate
- if (printStatus)
- {
- t2 = realTimeClock->GetCurrentStamp() - t1;
- MITK_INFO << " Framerate (fps): " << n / (t2/1000) << " Sequence: " << toFCameraDevice->m_ImageSequence;
- t1 = realTimeClock->GetCurrentStamp();
- printStatus = false;
- }
- } // end of while loop
- }
- return ITK_THREAD_RETURN_VALUE;
+ overflow = true;
+ }
+ if (m_ImageSequence % n == 0)
+ {
+ printStatus = true;
+ }
+ m_ImageMutex.unlock();
+ if (overflow)
+ {
+ overflow = false;
+ }
+ // print current framerate
+ if (printStatus)
+ {
+ t2 = realTimeClock->GetCurrentStamp() - t1;
+ MITK_INFO << " Framerate (fps): " << n / (t2/1000) << " Sequence: " << m_ImageSequence;
+ t1 = realTimeClock->GetCurrentStamp();
+ printStatus = false;
+ }
+ } // end of while loop
}
void ToFCameraMITKPlayerDevice::GetAmplitudes(float* amplitudeArray, int& imageSequence)
{
- m_ImageMutex->Lock();
+ m_ImageMutex.lock();
// write amplitude image data to float array
for (int i=0; i<this->m_PixelNumber; i++)
{
amplitudeArray[i] = this->m_AmplitudeDataBuffer[this->m_CurrentPos][i];
}
imageSequence = this->m_ImageSequence;
- m_ImageMutex->Unlock();
+ m_ImageMutex.unlock();
}
void ToFCameraMITKPlayerDevice::GetIntensities(float* intensityArray, int& imageSequence)
{
- m_ImageMutex->Lock();
+ m_ImageMutex.lock();
// write intensity image data to float array
for (int i=0; i<this->m_PixelNumber; i++)
{
intensityArray[i] = this->m_IntensityDataBuffer[this->m_CurrentPos][i];
}
imageSequence = this->m_ImageSequence;
- m_ImageMutex->Unlock();
+ m_ImageMutex.unlock();
}
void ToFCameraMITKPlayerDevice::GetDistances(float* distanceArray, int& imageSequence)
{
- m_ImageMutex->Lock();
+ m_ImageMutex.lock();
// write distance image data to float array
for (int i=0; i<this->m_PixelNumber; i++)
{
distanceArray[i] = this->m_DistanceDataBuffer[this->m_CurrentPos][i];
}
imageSequence = this->m_ImageSequence;
- m_ImageMutex->Unlock();
+ m_ImageMutex.unlock();
}
void ToFCameraMITKPlayerDevice::GetRgb(unsigned char* rgbArray, int& imageSequence)
{
- m_ImageMutex->Lock();
+ m_ImageMutex.lock();
// write intensity image data to unsigned char array
for (int i=0; i<this->m_RGBPixelNumber*3; i++)
{
rgbArray[i] = this->m_RGBDataBuffer[this->m_CurrentPos][i];
}
imageSequence = this->m_ImageSequence;
- m_ImageMutex->Unlock();
+ m_ImageMutex.unlock();
}
void ToFCameraMITKPlayerDevice::GetAllImages(float* distanceArray, float* amplitudeArray, float* intensityArray, char* /*sourceDataArray*/,
int requiredImageSequence, int& capturedImageSequence, unsigned char* rgbDataArray)
{
- m_ImageMutex->Lock();
+ m_ImageMutex.lock();
//check for empty buffer
if (this->m_ImageSequence < 0)
{
// buffer empty
MITK_INFO << "Buffer empty!! ";
capturedImageSequence = this->m_ImageSequence;
- m_ImageMutex->Unlock();
+ m_ImageMutex.unlock();
return;
}
// determine position of image in buffer
int pos = 0;
if ((requiredImageSequence < 0) || (requiredImageSequence > this->m_ImageSequence))
{
capturedImageSequence = this->m_ImageSequence;
pos = this->m_CurrentPos;
}
else if (requiredImageSequence <= this->m_ImageSequence - this->m_BufferSize)
{
capturedImageSequence = (this->m_ImageSequence - this->m_BufferSize) + 1;
pos = (this->m_CurrentPos + 1) % this->m_BufferSize;
}
else // (requiredImageSequence > this->m_ImageSequence - this->m_BufferSize) && (requiredImageSequence <= this->m_ImageSequence)
{
capturedImageSequence = requiredImageSequence;
pos = (this->m_CurrentPos + (10-(this->m_ImageSequence - requiredImageSequence))) % this->m_BufferSize;
}
if(this->m_DistanceDataBuffer&&this->m_AmplitudeDataBuffer&&this->m_IntensityDataBuffer&&this->m_RGBDataBuffer)
{
// write image data to float arrays
memcpy(distanceArray, this->m_DistanceDataBuffer[pos], this->m_PixelNumber * sizeof(float));
memcpy(amplitudeArray, this->m_AmplitudeDataBuffer[pos], this->m_PixelNumber * sizeof(float));
memcpy(intensityArray, this->m_IntensityDataBuffer[pos], this->m_PixelNumber * sizeof(float));
memcpy(rgbDataArray, this->m_RGBDataBuffer[pos], this->m_RGBPixelNumber * 3 * sizeof(unsigned char));
}
- m_ImageMutex->Unlock();
+ m_ImageMutex.unlock();
}
void ToFCameraMITKPlayerDevice::SetInputFileName(std::string inputFileName)
{
this->m_InputFileName = inputFileName;
this->m_Controller->SetInputFileName(inputFileName);
}
void ToFCameraMITKPlayerDevice::SetProperty( const char *propertyKey, BaseProperty* propertyValue )
{
this->m_PropertyList->SetProperty(propertyKey, propertyValue);
ToFCameraMITKPlayerController::Pointer myController = dynamic_cast<mitk::ToFCameraMITKPlayerController*>(this->m_Controller.GetPointer());
std::string strValue;
GetStringProperty(propertyKey, strValue);
if (strcmp(propertyKey, "DistanceImageFileName") == 0)
{
myController->SetDistanceImageFileName(strValue);
}
else if (strcmp(propertyKey, "AmplitudeImageFileName") == 0)
{
std::ifstream amplitudeImage(strValue.c_str());
if(amplitudeImage)
{
this->m_PropertyList->SetBoolProperty("HasAmplitudeImage", true);
myController->SetAmplitudeImageFileName(strValue);
}
else
{
MITK_WARN << "File " << strValue << " does not exist!";
}
}
else if (strcmp(propertyKey, "IntensityImageFileName") == 0)
{
std::ifstream intensityImage(strValue.c_str());
if(intensityImage)
{
this->m_PropertyList->SetBoolProperty("HasIntensityImage", true);
myController->SetIntensityImageFileName(strValue);
}
else
{
MITK_WARN << "File " << strValue << " does not exist!";
}
}
else if (strcmp(propertyKey, "RGBImageFileName") == 0)
{
std::ifstream intensityImage(strValue.c_str());
if(intensityImage)
{
this->m_PropertyList->SetBoolProperty("HasRGBImage", true);
myController->SetRGBImageFileName(strValue);
}
else
{
MITK_WARN << "File " << strValue << " does not exist!";
}
}
}
void ToFCameraMITKPlayerDevice::CleanUpDataBuffers()
{
if (m_DistanceDataBuffer)
{
for(int i=0; i<this->m_MaxBufferSize; i++)
{
delete[] this->m_DistanceDataBuffer[i];
}
delete[] this->m_DistanceDataBuffer;
}
if (m_AmplitudeDataBuffer)
{
for(int i=0; i<this->m_MaxBufferSize; i++)
{
delete[] this->m_AmplitudeDataBuffer[i];
}
delete[] this->m_AmplitudeDataBuffer;
}
if (m_IntensityDataBuffer)
{
for(int i=0; i<this->m_MaxBufferSize; i++)
{
delete[] this->m_IntensityDataBuffer[i];
}
delete[] this->m_IntensityDataBuffer;
}
if (m_RGBDataBuffer)
{
for(int i=0; i<this->m_MaxBufferSize; i++)
{
delete[] this->m_RGBDataBuffer[i];
}
delete[] this->m_RGBDataBuffer;
}
}
void ToFCameraMITKPlayerDevice::AllocateDataBuffers()
{
// free memory if it was already allocated
this->CleanUpDataBuffers();
// allocate buffers
this->m_DistanceDataBuffer = new float*[this->m_MaxBufferSize];
for(int i=0; i<this->m_MaxBufferSize; i++)
{
this->m_DistanceDataBuffer[i] = new float[this->m_PixelNumber];
}
this->m_AmplitudeDataBuffer = new float*[this->m_MaxBufferSize];
for(int i=0; i<this->m_MaxBufferSize; i++)
{
this->m_AmplitudeDataBuffer[i] = new float[this->m_PixelNumber];
}
this->m_IntensityDataBuffer = new float*[this->m_MaxBufferSize];
for(int i=0; i<this->m_MaxBufferSize; i++)
{
this->m_IntensityDataBuffer[i] = new float[this->m_PixelNumber];
}
this->m_RGBDataBuffer = new unsigned char*[this->m_MaxBufferSize];
for(int i=0; i<this->m_MaxBufferSize; i++)
{
this->m_RGBDataBuffer[i] = new unsigned char[this->m_RGBPixelNumber*3];
}
}
}
diff --git a/Modules/ToFHardware/mitkToFCameraMITKPlayerDevice.h b/Modules/ToFHardware/mitkToFCameraMITKPlayerDevice.h
index 52662df0c4..275265139a 100644
--- a/Modules/ToFHardware/mitkToFCameraMITKPlayerDevice.h
+++ b/Modules/ToFHardware/mitkToFCameraMITKPlayerDevice.h
@@ -1,138 +1,136 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef __mitkToFCameraMITKPlayerDevice_h
#define __mitkToFCameraMITKPlayerDevice_h
#include <MitkToFHardwareExports.h>
#include "mitkCommon.h"
#include "mitkToFCameraDevice.h"
#include "mitkToFCameraMITKPlayerController.h"
#include "itkObject.h"
#include "itkObjectFactory.h"
-#include "itkMultiThreader.h"
-#include "itkFastMutexLock.h"
namespace mitk
{
/**
* @brief Device class representing a player for MITK-ToF images.
*
* @ingroup ToFHardware
*/
class MITKTOFHARDWARE_EXPORT ToFCameraMITKPlayerDevice : public ToFCameraDevice
{
public:
mitkClassMacro( ToFCameraMITKPlayerDevice , ToFCameraDevice );
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/*!
\brief opens a connection to the ToF camera
*/
bool OnConnectCamera() override;
/*!
\brief closes the connection to the camera
*/
bool DisconnectCamera() override;
/*!
\brief starts the continuous updating of the camera.
A separate thread updates the source data, the main thread processes the source data and creates images and coordinates
*/
void StartCamera() override;
/*!
\brief gets the amplitude data from the ToF camera as the strength of the active illumination of every pixel. Caution! The user is responsible for allocating and deleting the images.
These values can be used to determine the quality of the distance values. The higher the amplitude value, the higher the accuracy of the according distance value
\param imageSequence the actually captured image sequence number
\param amplitudeArray contains the returned amplitude data as an array.
*/
void GetAmplitudes(float* amplitudeArray, int& imageSequence) override;
/*!
\brief gets the intensity data from the ToF camera as a greyscale image. Caution! The user is responsible for allocating and deleting the images.
\param intensityArray contains the returned intensities data as an array.
\param imageSequence the actually captured image sequence number
*/
void GetIntensities(float* intensityArray, int& imageSequence) override;
/*!
\brief gets the rgb data from the ToF camera. Caution! The user is responsible for allocating and deleting the images.
\param rgbArray contains the returned rgb data as an array.
\param imageSequence the actually captured image sequence number
*/
virtual void GetRgb(unsigned char* rgbArray, int& imageSequence);
/*!
\brief gets the distance data from the ToF camera measuring the distance between the camera and the different object points in millimeters. Caution! The user is responsible for allocating and deleting the images.
\param distanceArray contains the returned distances data as an array.
\param imageSequence the actually captured image sequence number
*/
void GetDistances(float* distanceArray, int& imageSequence) override;
/*!
\brief gets the 3 images (distance, amplitude, intensity) from the ToF camera. Caution! The user is responsible for allocating and deleting the images.
\param distanceArray contains the returned distance data as an array.
\param amplitudeArray contains the returned amplitude data as an array.
\param intensityArray contains the returned intensity data as an array.
\param sourceDataArray contains the complete source data from the camera device.
\param requiredImageSequence the required image sequence number
\param capturedImageSequence the actually captured image sequence number
\param rgbDataArray
*/
void GetAllImages(float* distanceArray, float* amplitudeArray, float* intensityArray, char* sourceDataArray,
int requiredImageSequence, int& capturedImageSequence, unsigned char* rgbDataArray=nullptr) override;
/*!
\brief Set file name where the data is recorded
\param inputFileName name of input file which should be played
*/
virtual void SetInputFileName(std::string inputFileName);
/*!
\brief set a BaseProperty
*/
void SetProperty( const char *propertyKey, BaseProperty* propertyValue ) override;
protected:
ToFCameraMITKPlayerDevice();
~ToFCameraMITKPlayerDevice() override;
/*!
\brief updates the camera for image acquisition
*/
void UpdateCamera() override;
/*!
\brief Thread method continuously acquiring images from the specified input file
*/
- static ITK_THREAD_RETURN_TYPE Acquire(void* pInfoStruct);
+ void Acquire();
/*!
\brief Clean up memory (pixel buffers)
*/
void CleanUpDataBuffers();
/*!
\brief Allocate pixel buffers
*/
void AllocateDataBuffers();
ToFCameraMITKPlayerController::Pointer m_Controller; ///< member holding the corresponding controller
std::string m_InputFileName; ///< member holding the file name of the current input file
private:
float** m_DistanceDataBuffer; ///< buffer holding the last distance images
float** m_AmplitudeDataBuffer; ///< buffer holding the last amplitude images
float** m_IntensityDataBuffer; ///< buffer holding the last intensity images
unsigned char** m_RGBDataBuffer; ///< buffer holding the last rgb images
};
} //END mitk namespace
#endif
diff --git a/Modules/ToFHardware/mitkToFImageRecorder.cpp b/Modules/ToFHardware/mitkToFImageRecorder.cpp
index f037ea1742..dc26e631d3 100644
--- a/Modules/ToFHardware/mitkToFImageRecorder.cpp
+++ b/Modules/ToFHardware/mitkToFImageRecorder.cpp
@@ -1,262 +1,243 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkToFImageRecorder.h"
#include <mitkRealTimeClock.h>
-#include <itkMultiThreader.h>
#include <itksys/SystemTools.hxx>
#pragma GCC visibility push(default)
#include <itkEventObject.h>
#pragma GCC visibility pop
namespace mitk
{
ToFImageRecorder::ToFImageRecorder()
{
this->m_ToFCameraDevice = nullptr;
- this->m_MultiThreader = itk::MultiThreader::New();
- this->m_AbortMutex = itk::FastMutexLock::New();
- this->m_ThreadID = 0;
this->m_NumOfFrames = 1; //lets record one frame per default
this->m_ToFImageWriter = nullptr;
this->m_DistanceImageSelected = true; //lets assume a device only has depth data by default
this->m_AmplitudeImageSelected = false;
this->m_IntensityImageSelected = false;
this->m_RGBImageSelected = false;
this->m_Abort = false;
this->m_ToFCaptureWidth = 0;
this->m_ToFCaptureHeight = 0;
this->m_RGBCaptureWidth = 0;
this->m_RGBCaptureHeight = 0;
this->m_FileFormat = ".nrrd"; //lets make nrrd the default
this->m_ToFPixelNumber = 0;
this->m_RGBPixelNumber = 0;
this->m_SourceDataSize = 0;
this->m_ToFImageType = ToFImageWriter::ToFImageType3D;
this->m_RecordMode = ToFImageRecorder::PerFrames;
this->m_DistanceImageFileName = "";
this->m_AmplitudeImageFileName = "";
this->m_IntensityImageFileName = "";
this->m_RGBImageFileName = "";
this->m_ImageSequence = 0;
this->m_DistanceArray = nullptr;
this->m_AmplitudeArray = nullptr;
this->m_IntensityArray = nullptr;
this->m_RGBArray = nullptr;
this->m_SourceDataArray = nullptr;
}
ToFImageRecorder::~ToFImageRecorder()
{
delete[] m_DistanceArray;
delete[] m_AmplitudeArray;
delete[] m_IntensityArray;
delete[] m_RGBArray;
delete[] m_SourceDataArray;
}
void ToFImageRecorder::StopRecording()
{
- this->m_AbortMutex->Lock();
+ this->m_AbortMutex.lock();
this->m_Abort = true;
- this->m_AbortMutex->Unlock();
+ this->m_AbortMutex.unlock();
}
void ToFImageRecorder::StartRecording()
{
if (this->m_ToFCameraDevice.IsNull())
{
throw std::invalid_argument("ToFCameraDevice is nullptr.");
return;
}
if (this->m_FileFormat.compare(".csv") == 0)
{
this->m_ToFImageWriter = ToFImageCsvWriter::New();
}
else if(this->m_FileFormat.compare(".nrrd") == 0)
{
this->m_ToFImageWriter = ToFNrrdImageWriter::New();
this->m_ToFImageWriter->SetExtension(m_FileFormat);
}
else
{
throw std::logic_error("No file format specified!");
}
this->m_RGBCaptureWidth = this->m_ToFCameraDevice->GetRGBCaptureWidth();
this->m_RGBCaptureHeight = this->m_ToFCameraDevice->GetRGBCaptureHeight();
this->m_RGBPixelNumber = this->m_RGBCaptureWidth * this->m_RGBCaptureHeight;
this->m_ToFCaptureWidth = this->m_ToFCameraDevice->GetCaptureWidth();
this->m_ToFCaptureHeight = this->m_ToFCameraDevice->GetCaptureHeight();
this->m_ToFPixelNumber = this->m_ToFCaptureWidth * this->m_ToFCaptureHeight;
this->m_SourceDataSize = this->m_ToFCameraDevice->GetSourceDataSize();
// allocate buffer
if(m_IntensityArray == nullptr)
{
this->m_IntensityArray = new float[m_ToFPixelNumber];
}
if(this->m_DistanceArray == nullptr)
{
this->m_DistanceArray = new float[m_ToFPixelNumber];
}
if(this->m_AmplitudeArray == nullptr)
{
this->m_AmplitudeArray = new float[m_ToFPixelNumber];
}
if(this->m_RGBArray == nullptr)
{
this->m_RGBArray = new unsigned char[m_RGBPixelNumber*3];
}
if(this->m_SourceDataArray == nullptr)
{
this->m_SourceDataArray = new char[m_SourceDataSize];
}
this->m_ToFImageWriter->SetDistanceImageFileName(this->m_DistanceImageFileName);
this->m_ToFImageWriter->SetAmplitudeImageFileName(this->m_AmplitudeImageFileName);
this->m_ToFImageWriter->SetIntensityImageFileName(this->m_IntensityImageFileName);
this->m_ToFImageWriter->SetRGBImageFileName(this->m_RGBImageFileName);
this->m_ToFImageWriter->SetRGBCaptureWidth(this->m_RGBCaptureWidth);
this->m_ToFImageWriter->SetRGBCaptureHeight(this->m_RGBCaptureHeight);
this->m_ToFImageWriter->SetToFCaptureWidth(this->m_ToFCaptureWidth);
this->m_ToFImageWriter->SetToFCaptureHeight(this->m_ToFCaptureHeight);
this->m_ToFImageWriter->SetToFImageType(this->m_ToFImageType);
this->m_ToFImageWriter->SetDistanceImageSelected(this->m_DistanceImageSelected);
this->m_ToFImageWriter->SetAmplitudeImageSelected(this->m_AmplitudeImageSelected);
this->m_ToFImageWriter->SetIntensityImageSelected(this->m_IntensityImageSelected);
this->m_ToFImageWriter->SetRGBImageSelected(this->m_RGBImageSelected);
this->m_ToFImageWriter->Open();
- this->m_AbortMutex->Lock();
+ this->m_AbortMutex.lock();
this->m_Abort = false;
- this->m_AbortMutex->Unlock();
- this->m_ThreadID = this->m_MultiThreader->SpawnThread(this->RecordData, this);
+ this->m_AbortMutex.unlock();
+ m_Thread = std::thread(&ToFImageRecorder::RecordData, this);
}
void ToFImageRecorder::WaitForThreadBeingTerminated()
{
- this->m_MultiThreader->TerminateThread(this->m_ThreadID);
+ if (m_Thread.joinable())
+ m_Thread.join();
}
-ITK_THREAD_RETURN_TYPE ToFImageRecorder::RecordData(void* pInfoStruct)
+void ToFImageRecorder::RecordData()
{
- struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct;
- if (pInfo == nullptr)
+ ToFCameraDevice::Pointer toFCameraDevice = this->GetCameraDevice();
+
+ mitk::RealTimeClock::Pointer realTimeClock;
+ realTimeClock = mitk::RealTimeClock::New();
+ int n = 100;
+ double t1 = 0;
+ double t2 = 0;
+ t1 = realTimeClock->GetCurrentStamp();
+ bool printStatus = false;
+ int requiredImageSequence = 0;
+ int numOfFramesRecorded = 0;
+
+ bool abort = false;
+ m_AbortMutex.lock();
+ abort = m_Abort;
+ m_AbortMutex.unlock();
+ while ( !abort )
{
- return ITK_THREAD_RETURN_VALUE;
- }
- if (pInfo->UserData == nullptr)
- {
- return ITK_THREAD_RETURN_VALUE;
- }
- ToFImageRecorder* toFImageRecorder = (ToFImageRecorder*)pInfo->UserData;
- if (toFImageRecorder!=nullptr)
- {
-
- ToFCameraDevice::Pointer toFCameraDevice = toFImageRecorder->GetCameraDevice();
-
- mitk::RealTimeClock::Pointer realTimeClock;
- realTimeClock = mitk::RealTimeClock::New();
- int n = 100;
- double t1 = 0;
- double t2 = 0;
- t1 = realTimeClock->GetCurrentStamp();
- bool printStatus = false;
- int requiredImageSequence = 0;
- int numOfFramesRecorded = 0;
-
- bool abort = false;
- toFImageRecorder->m_AbortMutex->Lock();
- abort = toFImageRecorder->m_Abort;
- toFImageRecorder->m_AbortMutex->Unlock();
- while ( !abort )
+ if ( ((m_RecordMode == ToFImageRecorder::PerFrames) && (numOfFramesRecorded < m_NumOfFrames)) ||
+ (m_RecordMode == ToFImageRecorder::Infinite) )
{
- if ( ((toFImageRecorder->m_RecordMode == ToFImageRecorder::PerFrames) && (numOfFramesRecorded < toFImageRecorder->m_NumOfFrames)) ||
- (toFImageRecorder->m_RecordMode == ToFImageRecorder::Infinite) )
- {
- toFCameraDevice->GetAllImages(toFImageRecorder->m_DistanceArray, toFImageRecorder->m_AmplitudeArray,
- toFImageRecorder->m_IntensityArray, toFImageRecorder->m_SourceDataArray, requiredImageSequence, toFImageRecorder->m_ImageSequence, toFImageRecorder->m_RGBArray );
+ toFCameraDevice->GetAllImages(m_DistanceArray, m_AmplitudeArray,
+ m_IntensityArray, m_SourceDataArray, requiredImageSequence, m_ImageSequence, m_RGBArray );
- if (toFImageRecorder->m_ImageSequence >= requiredImageSequence)
+ if (m_ImageSequence >= requiredImageSequence)
+ {
+ if (m_ImageSequence > requiredImageSequence)
{
- if (toFImageRecorder->m_ImageSequence > requiredImageSequence)
- {
- MITK_INFO << "Problem! required: " << requiredImageSequence << " captured: " << toFImageRecorder->m_ImageSequence;
- }
- requiredImageSequence = toFImageRecorder->m_ImageSequence + 1;
- toFImageRecorder->m_ToFImageWriter->Add( toFImageRecorder->m_DistanceArray,
- toFImageRecorder->m_AmplitudeArray, toFImageRecorder->m_IntensityArray, toFImageRecorder->m_RGBArray );
- numOfFramesRecorded++;
- if (numOfFramesRecorded % n == 0)
- {
- printStatus = true;
- }
- if (printStatus)
- {
- t2 = realTimeClock->GetCurrentStamp() - t1;
- MITK_INFO << " Framerate (fps): " << n / (t2/1000) << " Sequence: " << toFImageRecorder->m_ImageSequence;
- t1 = realTimeClock->GetCurrentStamp();
- printStatus = false;
- }
+ MITK_INFO << "Problem! required: " << requiredImageSequence << " captured: " << m_ImageSequence;
+ }
+ requiredImageSequence = m_ImageSequence + 1;
+ m_ToFImageWriter->Add(m_DistanceArray, m_AmplitudeArray, m_IntensityArray, m_RGBArray );
+ numOfFramesRecorded++;
+ if (numOfFramesRecorded % n == 0)
+ {
+ printStatus = true;
+ }
+ if (printStatus)
+ {
+ t2 = realTimeClock->GetCurrentStamp() - t1;
+ MITK_INFO << " Framerate (fps): " << n / (t2/1000) << " Sequence: " << m_ImageSequence;
+ t1 = realTimeClock->GetCurrentStamp();
+ printStatus = false;
}
- toFImageRecorder->m_AbortMutex->Lock();
- abort = toFImageRecorder->m_Abort;
- toFImageRecorder->m_AbortMutex->Unlock();
- }
- else
- {
- abort = true;
}
- } // end of while loop
+ m_AbortMutex.lock();
+ abort = m_Abort;
+ m_AbortMutex.unlock();
+ }
+ else
+ {
+ abort = true;
+ }
+ } // end of while loop
- toFImageRecorder->InvokeEvent(itk::AbortEvent());
+ this->InvokeEvent(itk::AbortEvent());
- toFImageRecorder->m_ToFImageWriter->Close();
- }
- return ITK_THREAD_RETURN_VALUE;
+ m_ToFImageWriter->Close();
}
void ToFImageRecorder::SetCameraDevice(ToFCameraDevice* aToFCameraDevice)
{
this->m_ToFCameraDevice = aToFCameraDevice;
}
ToFCameraDevice* ToFImageRecorder::GetCameraDevice()
{
return this->m_ToFCameraDevice;
}
ToFImageWriter::ToFImageType ToFImageRecorder::GetToFImageType()
{
return this->m_ToFImageType;
}
void ToFImageRecorder::SetToFImageType(ToFImageWriter::ToFImageType toFImageType)
{
this->m_ToFImageType = toFImageType;
}
ToFImageRecorder::RecordMode ToFImageRecorder::GetRecordMode()
{
return this->m_RecordMode;
}
void ToFImageRecorder::SetRecordMode(ToFImageRecorder::RecordMode recordMode)
{
this->m_RecordMode = recordMode;
}
}
diff --git a/Modules/ToFHardware/mitkToFImageRecorder.h b/Modules/ToFHardware/mitkToFImageRecorder.h
index 16e022f396..e9ea462700 100644
--- a/Modules/ToFHardware/mitkToFImageRecorder.h
+++ b/Modules/ToFHardware/mitkToFImageRecorder.h
@@ -1,177 +1,178 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkToFImageRecorder_h
#define mitkToFImageRecorder_h
#include "MitkToFHardwareExports.h"
#include <mitkCommon.h>
#include "mitkToFCameraDevice.h"
#include "mitkToFImageCsvWriter.h"
#include "mitkToFNrrdImageWriter.h"
#include <itkObject.h>
#include <itkObjectFactory.h>
-#include <itkFastMutexLock.h>
#include <itkCommand.h>
+#include <mutex>
+#include <thread>
+
namespace mitk
{
/**
* @brief Recorder class for ToF images
*
* This class represents a recorder for ToF data. A ToFCameraDevice is used to acquire the data. The acquired images
* are then added to a ToFImageWriter that performs the actual writing.
*
* Recording can be performed either frame-based or continuously.
*
* @warning It is currently not guaranteed that all acquired images are recorded, since the recording
* is done in a newly spawned thread. However, in practise only very few images are lost. See bug #12997
* for more details.
*
* @ingroup ToFHardware
*/
class MITKTOFHARDWARE_EXPORT ToFImageRecorder : public itk::Object
{
public:
ToFImageRecorder();
~ToFImageRecorder() override;
mitkClassMacroItkParent( ToFImageRecorder , itk::Object );
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
itkGetMacro( DistanceImageFileName, std::string );
itkGetMacro( AmplitudeImageFileName, std::string );
itkGetMacro( IntensityImageFileName, std::string );
itkGetMacro( RGBImageFileName, std::string );
itkGetMacro( ToFCaptureWidth, int );
itkGetMacro( ToFCaptureHeight, int );
itkGetMacro( RGBCaptureWidth, int );
itkGetMacro( RGBCaptureHeight, int );
itkGetMacro( DistanceImageSelected, bool );
itkGetMacro( AmplitudeImageSelected, bool );
itkGetMacro( IntensityImageSelected, bool );
itkGetMacro( RGBImageSelected, bool );
itkGetMacro( NumOfFrames, int );
itkGetMacro( FileFormat, std::string );
itkSetMacro( DistanceImageFileName, std::string );
itkSetMacro( AmplitudeImageFileName, std::string );
itkSetMacro( IntensityImageFileName, std::string );
itkSetMacro(RGBImageFileName, std::string );
itkSetMacro( DistanceImageSelected, bool );
itkSetMacro( AmplitudeImageSelected, bool );
itkSetMacro( IntensityImageSelected, bool );
itkSetMacro( RGBImageSelected, bool );
itkSetMacro( NumOfFrames, int );
itkSetMacro( FileFormat, std::string );
enum RecordMode{ PerFrames, Infinite };
/*!
\brief Returns the currently set RecordMode
\return record mode: PerFrames ("Record specified number of frames"), Infinite ("Record until abort is required")
*/
ToFImageRecorder::RecordMode GetRecordMode();
/*!
\brief Set the RecordMode
\param recordMode: PerFrames ("Record specified number of frames"), Infinite ("Record until abort is required")
*/
void SetRecordMode(ToFImageRecorder::RecordMode recordMode);
/*!
\brief Set the device used for acquiring ToF images
\param aToFCameraDevice ToF camera device used.
*/
void SetCameraDevice(ToFCameraDevice* aToFCameraDevice);
/*!
\brief Get the device used for acquiring ToF images
\return ToF camera device used.
*/
ToFCameraDevice* GetCameraDevice();
/*!
\brief Get the type of image to be recorded
\return ToF image type: ToFImageType3D (0) or ToFImageType2DPlusT (1)
*/
ToFImageWriter::ToFImageType GetToFImageType();
/*!
\brief Set the type of image to be recorded
\param toFImageType type of the ToF image: ToFImageType3D (0) or ToFImageType2DPlusT (1)
*/
void SetToFImageType(ToFImageWriter::ToFImageType toFImageType);
/*!
\brief Starts the recording by spawning a Thread which streams the data to a file. Aborting of the record process is controlled by the m_Abort flag
*/
void StartRecording();
/*!
\brief Stops the recording by setting the m_Abort flag to false
*/
void StopRecording();
/*!
\brief Wait until thread is terinated
*/
void WaitForThreadBeingTerminated();
protected:
/*!
\brief Thread method acquiring data via the ToFCameraDevice and recording it to file via the ToFImageWriter
*/
- static ITK_THREAD_RETURN_TYPE RecordData(void* pInfoStruct);
+ void RecordData();
// data acquisition
ToFCameraDevice::Pointer m_ToFCameraDevice; ///< ToFCameraDevice used for acquiring the images
int m_ToFCaptureWidth; ///< width (x-dimension) of the images to record.
int m_ToFCaptureHeight; ///< height (y-dimension) of the images to record.
int m_ToFPixelNumber; ///< number of pixels (widht*height) of the images to record
int m_RGBCaptureWidth; ///< width (x-dimension) of the images to record.
int m_RGBCaptureHeight; ///< height (y-dimension) of the images to record.
int m_RGBPixelNumber; ///< number of pixels (widht*height) of the images to record
int m_SourceDataSize; ///< size of the source data provided by the device
int m_ImageSequence; ///< number of images currently acquired
float* m_IntensityArray; ///< array holding the intensity data
float* m_DistanceArray; ///< array holding the distance data
float* m_AmplitudeArray; ///< array holding the amplitude data
unsigned char* m_RGBArray; ///< array holding the RGB data if available (e.g. for Kinect)
char* m_SourceDataArray; ///< array holding the source data
// data writing
ToFImageWriter::Pointer m_ToFImageWriter; ///< image writer writing the acquired images to a file
std::string m_DistanceImageFileName; ///< file name for saving the distance image
std::string m_AmplitudeImageFileName; ///< file name for saving the amplitude image
std::string m_IntensityImageFileName; ///< file name for saving the intensity image
std::string m_RGBImageFileName; ///< file name for saving the rgb image
int m_NumOfFrames; ///< number of frames to be recorded by this recorder
ToFImageWriter::ToFImageType m_ToFImageType; ///< type of image to be recorded: ToFImageType3D (0) or ToFImageType2DPlusT (1)
ToFImageRecorder::RecordMode m_RecordMode; ///< mode of recording the images: specified number of frames (PerFrames) or infinite (Infinite)
std::string m_FileFormat; ///< file format for saving images. If .csv is chosen, ToFImageCsvWriter is used
bool m_DistanceImageSelected; ///< flag indicating if distance image should be recorded
bool m_AmplitudeImageSelected; ///< flag indicating if amplitude image should be recorded
bool m_IntensityImageSelected; ///< flag indicating if intensity image should be recorded
bool m_RGBImageSelected; ///< flag indicating if rgb image should be recorded
// threading
- itk::MultiThreader::Pointer m_MultiThreader; ///< member for thread-handling (ITK-based)
- int m_ThreadID; ///< ID of the thread recording the data
- itk::FastMutexLock::Pointer m_AbortMutex; ///< mutex for thread-safe data access of abort flag
+ std::thread m_Thread;
+ std::mutex m_AbortMutex; ///< mutex for thread-safe data access of abort flag
bool m_Abort; ///< flag controlling the abort mechanism of the recording procedure. For thread-safety only use in combination with m_AbortMutex
private:
};
} //END mitk namespace
#endif //mitkToFImageRecorder_h
diff --git a/Modules/ToFHardware/mitkToFNrrdImageWriter.cpp b/Modules/ToFHardware/mitkToFNrrdImageWriter.cpp
index 77174ebeda..356d726d67 100644
--- a/Modules/ToFHardware/mitkToFNrrdImageWriter.cpp
+++ b/Modules/ToFHardware/mitkToFNrrdImageWriter.cpp
@@ -1,276 +1,276 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// mitk includes
#include <mitkToFNrrdImageWriter.h>
// itk includes
#include "itksys/SystemTools.hxx"
#include "itkNrrdImageIO.h"
namespace mitk
{
ToFNrrdImageWriter::ToFNrrdImageWriter(): ToFImageWriter(),
m_DistanceOutfile(), m_AmplitudeOutfile(), m_IntensityOutfile()
{
m_Extension = std::string(".nrrd");
}
ToFNrrdImageWriter::~ToFNrrdImageWriter()
{
}
void ToFNrrdImageWriter::Open()
{
this->CheckForFileExtension(this->m_DistanceImageFileName);
this->CheckForFileExtension(this->m_AmplitudeImageFileName);
this->CheckForFileExtension(this->m_IntensityImageFileName);
this->CheckForFileExtension(this->m_RGBImageFileName);
this->m_ToFPixelNumber = this->m_ToFCaptureWidth * this->m_ToFCaptureHeight;
this->m_ToFImageSizeInBytes = this->m_ToFPixelNumber * sizeof(float);
this->m_RGBPixelNumber = this->m_RGBCaptureWidth * this->m_RGBCaptureHeight;
this->m_RGBImageSizeInBytes = this->m_RGBPixelNumber * sizeof(unsigned char) * 3;
if (this->m_DistanceImageSelected)
{
this->OpenStreamFile( this->m_DistanceOutfile, this->m_DistanceImageFileName);
}
if (this->m_AmplitudeImageSelected)
{
this->OpenStreamFile(this->m_AmplitudeOutfile, this->m_AmplitudeImageFileName);
}
if (this->m_IntensityImageSelected)
{
this->OpenStreamFile(this->m_IntensityOutfile, this->m_IntensityImageFileName);
}
if (this->m_RGBImageSelected)
{
this->OpenStreamFile(this->m_RGBOutfile, this->m_RGBImageFileName);
}
this->m_NumOfFrames = 0;
}
void ToFNrrdImageWriter::Close()
{
if (this->m_DistanceImageSelected)
{
this->CloseStreamFile(this->m_DistanceOutfile, this->m_DistanceImageFileName);
}
if (this->m_AmplitudeImageSelected)
{
this->CloseStreamFile(this->m_AmplitudeOutfile, this->m_AmplitudeImageFileName);
}
if (this->m_IntensityImageSelected)
{
this->CloseStreamFile(this->m_IntensityOutfile, this->m_IntensityImageFileName);
}
if (this->m_RGBImageSelected)
{
this->CloseStreamFile(this->m_RGBOutfile, this->m_RGBImageFileName);
}
}
void ToFNrrdImageWriter::Add(float* distanceFloatData, float* amplitudeFloatData, float* intensityFloatData, unsigned char* rgbData)
{
if (this->m_DistanceImageSelected)
{
this->m_DistanceOutfile.write( (char*) distanceFloatData, this->m_ToFImageSizeInBytes);
}
if (this->m_AmplitudeImageSelected)
{
this->m_AmplitudeOutfile.write( (char*)amplitudeFloatData, this->m_ToFImageSizeInBytes);
}
if (this->m_IntensityImageSelected)
{
this->m_IntensityOutfile.write(( char* )intensityFloatData, this->m_ToFImageSizeInBytes);
}
if (this->m_RGBImageSelected)
{
this->m_RGBOutfile.write(( char* )rgbData, this->m_RGBImageSizeInBytes);
}
this->m_NumOfFrames++;
}
void ToFNrrdImageWriter::OpenStreamFile( std::ofstream &outfile, std::string outfileName )
{
outfile.open(outfileName.c_str(), std::ofstream::binary);
if(!outfile.is_open())
{
MITK_ERROR << "Error opening outfile: " << outfileName;
throw std::logic_error("Error opening outfile.");
return;
}
}
void ToFNrrdImageWriter::CloseStreamFile( std::ofstream &outfile, std::string fileName )
{
if (this->m_NumOfFrames == 0)
{
outfile.close();
throw std::logic_error("File is empty.");
return;
}
// flush the last data to the file and convert the stream data to nrrd file
outfile.flush();
this->ConvertStreamToNrrdFormat( fileName );
outfile.close();
}
void ToFNrrdImageWriter::ConvertStreamToNrrdFormat( std::string fileName )
{
int CaptureWidth = 0;
int CaptureHeight = 0;
int PixelNumber = 0;
if (fileName==this->m_RGBImageFileName)
{
CaptureWidth = this->m_RGBCaptureWidth;
CaptureHeight = this->m_RGBCaptureHeight;
PixelNumber = this->m_RGBPixelNumber;
}
else
{
CaptureWidth = this->m_ToFCaptureWidth;
CaptureHeight = this->m_ToFCaptureHeight;
PixelNumber = this->m_ToFPixelNumber;
}
Image::Pointer imageTemplate = Image::New();
unsigned int dimension;
unsigned int* dimensions;
if(m_ToFImageType == ToFImageType2DPlusT)
{
dimension = 4;
dimensions = new unsigned int[dimension];
dimensions[0] = CaptureWidth;
dimensions[1] = CaptureHeight;
dimensions[2] = 1;
dimensions[3] = this->m_NumOfFrames;
}
else if( m_ToFImageType == ToFImageType3D)
{
dimension = 3;
dimensions = new unsigned int[dimension];
dimensions[0] = CaptureWidth;
dimensions[1] = CaptureHeight;
dimensions[2] = this->m_NumOfFrames;
}
else
{
throw std::logic_error("No image type set, please choose between 2D+t and 3D!");
}
float* floatData = nullptr;
unsigned char* rgbData = nullptr;
if (fileName==this->m_RGBImageFileName)
{
rgbData = new unsigned char[PixelNumber*3];
for(int i=0; i<PixelNumber*3; i++)
{
rgbData[i] = i + 0.0;
}
mitk::PixelType RGBType = MakePixelType<unsigned char, itk::RGBPixel<unsigned char>, 3>();
imageTemplate->Initialize( RGBType,dimension, dimensions, 1);
imageTemplate->SetSlice(rgbData, 0, 0, 0);
}
else
{
floatData = new float[PixelNumber];
for(int i=0; i<PixelNumber; i++)
{
floatData[i] = i + 0.0;
}
mitk::PixelType FloatType = MakeScalarPixelType<float>();
imageTemplate->Initialize( FloatType,dimension, dimensions, 1);
imageTemplate->SetSlice(floatData, 0, 0, 0);
}
itk::NrrdImageIO::Pointer nrrdWriter = itk::NrrdImageIO::New();
nrrdWriter->SetNumberOfDimensions(dimension);
nrrdWriter->SetPixelType( imageTemplate->GetPixelType().GetPixelType());
- nrrdWriter->SetComponentType( (itk::ImageIOBase::IOComponentType) imageTemplate->GetPixelType().GetComponentType());
+ nrrdWriter->SetComponentType( imageTemplate->GetPixelType().GetComponentType());
if(imageTemplate->GetPixelType().GetNumberOfComponents() > 1)
{
nrrdWriter->SetNumberOfComponents(imageTemplate->GetPixelType().GetNumberOfComponents());
}
itk::ImageIORegion ioRegion( dimension );
mitk::Vector3D spacing = imageTemplate->GetGeometry()->GetSpacing();
mitk::Point3D origin = imageTemplate->GetGeometry()->GetOrigin();
for(unsigned int i = 0; i < dimension; i++)
{
nrrdWriter->SetDimensions(i,dimensions[i]);
nrrdWriter->SetSpacing(i,spacing[i]);
nrrdWriter->SetOrigin(i,origin[i]);
- mitk::Vector3D direction;
- direction.SetVnlVector(imageTemplate->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i));
+ mitk::Vector3D direction(0.0);
+ direction.SetVnlVector(imageTemplate->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i).as_ref());
vnl_vector< double > axisDirection(dimension);
for(unsigned int j = 0; j < dimension; j++)
{
axisDirection[j] = direction[j]/spacing[i];
}
nrrdWriter->SetDirection( i, axisDirection );
ioRegion.SetSize(i, imageTemplate->GetLargestPossibleRegion().GetSize(i) );
ioRegion.SetIndex(i, imageTemplate->GetLargestPossibleRegion().GetIndex(i) );
}
nrrdWriter->SetIORegion(ioRegion);
nrrdWriter->SetFileName(fileName);
nrrdWriter->SetUseStreamedWriting(true);
std::ifstream stream(fileName.c_str(), std::ifstream::binary);
if (fileName==m_RGBImageFileName)
{
unsigned int size = PixelNumber*3 * this->m_NumOfFrames;
unsigned int sizeInBytes = size * sizeof(unsigned char);
unsigned char* data = new unsigned char[size];
stream.read((char*)data, sizeInBytes);
nrrdWriter->Write(data);
stream.close();
delete[] data;
}
else
{
unsigned int size = PixelNumber * this->m_NumOfFrames;
unsigned int sizeInBytes = size * sizeof(float);
float* data = new float[size];
stream.read((char*)data, sizeInBytes);
try
{
nrrdWriter->Write(data);
}
catch (itk::ExceptionObject* e)
{
MITK_ERROR<< e->what();
return;
}
stream.close();
delete[] data;
}
delete[] dimensions;
if (fileName==m_RGBImageFileName)
{
delete[] rgbData;
}
else
{
delete[] floatData;
}
}
} // end namespace mitk
diff --git a/Modules/ToFProcessing/mitkToFCompositeFilter.cpp b/Modules/ToFProcessing/mitkToFCompositeFilter.cpp
index 638ba1c82e..e7306d8edc 100644
--- a/Modules/ToFProcessing/mitkToFCompositeFilter.cpp
+++ b/Modules/ToFProcessing/mitkToFCompositeFilter.cpp
@@ -1,398 +1,398 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <mitkToFCompositeFilter.h>
#include <mitkInstantiateAccessFunctions.h>
#include "mitkImageReadAccessor.h"
#include <itkImage.h>
#include "opencv2/imgproc.hpp"
mitk::ToFCompositeFilter::ToFCompositeFilter() : m_SegmentationMask(nullptr), m_ImageWidth(0), m_ImageHeight(0), m_ImageSize(0),
m_IplDistanceImage(nullptr), m_IplOutputImage(nullptr), m_ItkInputImage(nullptr), m_ApplyTemporalMedianFilter(false), m_ApplyAverageFilter(false),
m_ApplyMedianFilter(false), m_ApplyThresholdFilter(false), m_ApplyMaskSegmentation(false), m_ApplyBilateralFilter(false), m_DataBuffer(nullptr),
m_DataBufferCurrentIndex(0), m_DataBufferMaxSize(0), m_TemporalMedianFilterNumOfFrames(10), m_ThresholdFilterMin(1),
m_ThresholdFilterMax(7000), m_BilateralFilterDomainSigma(2), m_BilateralFilterRangeSigma(60), m_BilateralFilterKernelRadius(0)
{
}
mitk::ToFCompositeFilter::~ToFCompositeFilter()
{
cvReleaseImage(&(this->m_IplDistanceImage));
cvReleaseImage(&(this->m_IplOutputImage));
if (m_DataBuffer!=nullptr)
{
delete [] m_DataBuffer;
}
}
void mitk::ToFCompositeFilter::SetInput( const InputImageType* distanceImage )
{
this->SetInput(0, distanceImage);
}
void mitk::ToFCompositeFilter::SetInput( unsigned int idx, const InputImageType* distanceImage )
{
if ((distanceImage == nullptr) && (idx == this->GetNumberOfInputs() - 1)) // if the last input is set to nullptr, reduce the number of inputs by one
{
this->SetNumberOfIndexedInputs(this->GetNumberOfInputs() - 1);
}
else
{
if (idx==0) //create IPL image holding distance data
{
if (!distanceImage->IsEmpty())
{
this->m_ImageWidth = distanceImage->GetDimension(0);
this->m_ImageHeight = distanceImage->GetDimension(1);
this->m_ImageSize = this->m_ImageWidth * this->m_ImageHeight * sizeof(float);
if (this->m_IplDistanceImage != nullptr)
{
cvReleaseImage(&(this->m_IplDistanceImage));
}
ImageReadAccessor distImgAcc(distanceImage, distanceImage->GetSliceData(0,0,0));
float* distanceFloatData = (float*) distImgAcc.GetData();
this->m_IplDistanceImage = cvCreateImage(cvSize(this->m_ImageWidth, this->m_ImageHeight), IPL_DEPTH_32F, 1);
memcpy(this->m_IplDistanceImage->imageData, (void*)distanceFloatData, this->m_ImageSize);
if (this->m_IplOutputImage != nullptr)
{
cvReleaseImage(&(this->m_IplOutputImage));
}
this->m_IplOutputImage = cvCreateImage(cvSize(this->m_ImageWidth, this->m_ImageHeight), IPL_DEPTH_32F, 1);
CreateItkImage(this->m_ItkInputImage);
}
}
this->ProcessObject::SetNthInput(idx, const_cast<InputImageType*>(distanceImage)); // Process object is not const-correct so the const_cast is required here
}
this->CreateOutputsForAllInputs();
}
mitk::Image* mitk::ToFCompositeFilter::GetInput()
{
return this->GetInput(0);
}
mitk::Image* mitk::ToFCompositeFilter::GetInput( unsigned int idx )
{
if (this->GetNumberOfInputs() < 1)
return nullptr; //TODO: geeignete exception werfen
return static_cast< mitk::Image*>(this->ProcessObject::GetInput(idx));
}
void mitk::ToFCompositeFilter::GenerateData()
{
// copy input 1...n to output 1...n
for (unsigned int idx=0; idx<this->GetNumberOfOutputs(); idx++)
{
mitk::Image::Pointer outputImage = this->GetOutput(idx);
mitk::Image::Pointer inputImage = this->GetInput(idx);
if (outputImage.IsNotNull()&&inputImage.IsNotNull())
{
ImageReadAccessor inputAcc(inputImage, inputImage->GetSliceData());
outputImage->CopyInformation(inputImage);
outputImage->Initialize(inputImage->GetPixelType(),inputImage->GetDimension(),inputImage->GetDimensions());
outputImage->SetSlice(inputAcc.GetData());
}
}
//mitk::Image::Pointer outputDistanceImage = this->GetOutput();
ImageReadAccessor outputAcc(this->GetOutput(), this->GetOutput()->GetSliceData(0, 0, 0) );
float* outputDistanceFloatData = (float*) outputAcc.GetData();
//mitk::Image::Pointer inputDistanceImage = this->GetInput();
ImageReadAccessor inputAcc(this->GetInput(), this->GetInput()->GetSliceData(0, 0, 0) );
// copy initial distance image to ipl image
float* distanceFloatData = (float*)inputAcc.GetData();
memcpy(this->m_IplDistanceImage->imageData, (void*)distanceFloatData, this->m_ImageSize);
if (m_ApplyThresholdFilter||m_ApplyMaskSegmentation)
{
ProcessSegmentation(this->m_IplDistanceImage);
}
if (this->m_ApplyTemporalMedianFilter||this->m_ApplyAverageFilter)
{
ProcessStreamedQuickSelectMedianImageFilter(this->m_IplDistanceImage);
}
if (this->m_ApplyMedianFilter)
{
ProcessCVMedianFilter(this->m_IplDistanceImage, this->m_IplOutputImage);
memcpy( this->m_IplDistanceImage->imageData, this->m_IplOutputImage->imageData, this->m_ImageSize );
}
if (this->m_ApplyBilateralFilter)
{
float* itkFloatData = this->m_ItkInputImage->GetBufferPointer();
memcpy(itkFloatData, this->m_IplDistanceImage->imageData, this->m_ImageSize );
ItkImageType2D::Pointer itkOutputImage = ProcessItkBilateralFilter(this->m_ItkInputImage);
memcpy( this->m_IplDistanceImage->imageData, itkOutputImage->GetBufferPointer(), this->m_ImageSize );
//ProcessCVBilateralFilter(this->m_IplDistanceImage, this->m_OutputIplImage, domainSigma, rangeSigma, kernelRadius);
//memcpy( distanceFloatData, this->m_OutputIplImage->imageData, distanceImageSize );
}
memcpy( outputDistanceFloatData, this->m_IplDistanceImage->imageData, this->m_ImageSize );
}
void mitk::ToFCompositeFilter::CreateOutputsForAllInputs()
{
this->SetNumberOfIndexedOutputs(this->GetNumberOfInputs()); // create outputs for all inputs
for (unsigned int idx = 0; idx < this->GetNumberOfIndexedInputs(); ++idx)
{
if (this->GetOutput(idx) == nullptr)
{
DataObjectPointer newOutput = this->MakeOutput(idx);
this->SetNthOutput(idx, newOutput);
}
}
this->Modified();
}
void mitk::ToFCompositeFilter::GenerateOutputInformation()
{
mitk::Image::ConstPointer input = this->GetInput();
mitk::Image::Pointer output = this->GetOutput();
if (output->IsInitialized())
return;
itkDebugMacro(<<"GenerateOutputInformation()");
output->Initialize(input->GetPixelType(), *input->GetTimeGeometry());
output->SetPropertyList(input->GetPropertyList()->Clone());
}
void mitk::ToFCompositeFilter::ProcessSegmentation(IplImage* inputIplImage)
{
char* segmentationMask;
if (m_SegmentationMask.IsNotNull())
{
ImageReadAccessor segMaskAcc(m_SegmentationMask, m_SegmentationMask->GetSliceData(0,0,0));
segmentationMask = (char*)segMaskAcc.GetData();
}
else
{
segmentationMask = nullptr;
}
float *f = (float*)inputIplImage->imageData;
for(int i=0; i<this->m_ImageWidth*this->m_ImageHeight; i++)
{
if (this->m_ApplyThresholdFilter)
{
if (f[i]<=m_ThresholdFilterMin)
{
f[i] = 0.0;
}
else if (f[i]>=m_ThresholdFilterMax)
{
f[i] = 0.0;
}
}
if (this->m_ApplyMaskSegmentation)
{
if (segmentationMask)
{
if (segmentationMask[i]==0)
{
f[i] = 0.0;
}
}
}
}
}
ItkImageType2D::Pointer mitk::ToFCompositeFilter::ProcessItkBilateralFilter(ItkImageType2D::Pointer inputItkImage)
{
ItkImageType2D::Pointer outputItkImage;
BilateralFilterType::Pointer bilateralFilter = BilateralFilterType::New();
bilateralFilter->SetInput(inputItkImage);
bilateralFilter->SetDomainSigma(m_BilateralFilterDomainSigma);
bilateralFilter->SetRangeSigma(m_BilateralFilterRangeSigma);
//bilateralFilter->SetRadius(m_BilateralFilterKernelRadius);
outputItkImage = bilateralFilter->GetOutput();
outputItkImage->Update();
return outputItkImage;
}
void mitk::ToFCompositeFilter::ProcessCVBilateralFilter(IplImage* inputIplImage, IplImage* outputIplImage)
{
int diameter = m_BilateralFilterKernelRadius;
double sigmaColor = m_BilateralFilterRangeSigma;
double sigmaSpace = m_BilateralFilterDomainSigma;
cvSmooth(inputIplImage, outputIplImage, CV_BILATERAL, diameter, 0, sigmaColor, sigmaSpace);
}
void mitk::ToFCompositeFilter::ProcessCVMedianFilter(IplImage* inputIplImage, IplImage* outputIplImage, int radius)
{
cvSmooth(inputIplImage, outputIplImage, CV_MEDIAN, radius, 0, 0, 0);
}
void mitk::ToFCompositeFilter::ProcessStreamedQuickSelectMedianImageFilter(IplImage* inputIplImage)
{
float* data = (float*)inputIplImage->imageData;
int imageSize = inputIplImage->width * inputIplImage->height;
float* tmpArray;
if (this->m_TemporalMedianFilterNumOfFrames == 0)
{
return;
}
if (m_TemporalMedianFilterNumOfFrames != this->m_DataBufferMaxSize) // reset
{
//delete current buffer
for( int i=0; i<this->m_DataBufferMaxSize; i++ ) {
delete[] this->m_DataBuffer[i];
}
if (this->m_DataBuffer != nullptr)
{
delete[] this->m_DataBuffer;
}
this->m_DataBufferMaxSize = m_TemporalMedianFilterNumOfFrames;
// create new buffer with current size
this->m_DataBuffer = new float*[this->m_DataBufferMaxSize];
for(int i=0; i<this->m_DataBufferMaxSize; i++)
{
this->m_DataBuffer[i] = nullptr;
}
this->m_DataBufferCurrentIndex = 0;
}
int currentBufferSize = this->m_DataBufferMaxSize;
tmpArray = new float[this->m_DataBufferMaxSize];
// copy data to buffer
if (this->m_DataBuffer[this->m_DataBufferCurrentIndex] == nullptr)
{
this->m_DataBuffer[this->m_DataBufferCurrentIndex] = new float[imageSize];
currentBufferSize = this->m_DataBufferCurrentIndex + 1;
}
for(int j=0; j<imageSize; j++)
{
this->m_DataBuffer[this->m_DataBufferCurrentIndex][j] = data[j];
}
float tmpValue = 0.0f;
for(int i=0; i<imageSize; i++)
{
if (m_ApplyAverageFilter)
{
tmpValue = 0.0f;
for(int j=0; j<currentBufferSize; j++)
{
tmpValue+=this->m_DataBuffer[j][i];
}
data[i] = tmpValue/currentBufferSize;
}
else if (m_ApplyTemporalMedianFilter)
{
for(int j=0; j<currentBufferSize; j++)
{
tmpArray[j] = this->m_DataBuffer[j][i];
}
data[i] = quick_select(tmpArray, currentBufferSize);
}
}
this->m_DataBufferCurrentIndex = (this->m_DataBufferCurrentIndex + 1) % this->m_DataBufferMaxSize;
delete[] tmpArray;
}
-#define ELEM_SWAP(a,b) { register float t=(a);(a)=(b);(b)=t; }
+#define ELEM_SWAP(a,b) { float t=(a);(a)=(b);(b)=t; }
float mitk::ToFCompositeFilter::quick_select(float arr[], int n)
{
int low = 0;
int high = n-1;
int median = (low + high)/2;
int middle = 0;
int ll = 0;
int hh = 0;
for (;;) {
if (high <= low) /* One element only */
return arr[median] ;
if (high == low + 1) { /* Two elements only */
if (arr[low] > arr[high])
ELEM_SWAP(arr[low], arr[high]) ;
return arr[median] ;
}
/* Find median of low, middle and high items; swap into position low */
middle = (low + high) / 2;
if (arr[middle] > arr[high]) ELEM_SWAP(arr[middle], arr[high]) ;
if (arr[low] > arr[high]) ELEM_SWAP(arr[low], arr[high]) ;
if (arr[middle] > arr[low]) ELEM_SWAP(arr[middle], arr[low]) ;
/* Swap low item (now in position middle) into position (low+1) */
ELEM_SWAP(arr[middle], arr[low+1]) ;
/* Nibble from each end towards middle, swapping items when stuck */
ll = low + 1;
hh = high;
for (;;) {
do ll++; while (arr[low] > arr[ll]) ;
do hh--; while (arr[hh] > arr[low]) ;
if (hh < ll)
break;
ELEM_SWAP(arr[ll], arr[hh]) ;
}
/* Swap middle item (in position low) back into correct position */
ELEM_SWAP(arr[low], arr[hh]) ;
/* Re-set active partition */
if (hh <= median)
low = ll;
if (hh >= median)
high = hh - 1;
}
}
#undef ELEM_SWAP
void mitk::ToFCompositeFilter::SetTemporalMedianFilterParameter(int tmporalMedianFilterNumOfFrames)
{
this->m_TemporalMedianFilterNumOfFrames = tmporalMedianFilterNumOfFrames;
}
void mitk::ToFCompositeFilter::SetThresholdFilterParameter(int min, int max)
{
if (min > max)
{
min = max;
}
this->m_ThresholdFilterMin = min;
this->m_ThresholdFilterMax = max;
}
void mitk::ToFCompositeFilter::SetBilateralFilterParameter(double domainSigma, double rangeSigma, int kernelRadius = 0)
{
this->m_BilateralFilterDomainSigma = domainSigma;
this->m_BilateralFilterRangeSigma = rangeSigma;
this->m_BilateralFilterKernelRadius = kernelRadius;
}
void mitk::ToFCompositeFilter::CreateItkImage(ItkImageType2D::Pointer &itkInputImage)
{
itkInputImage = ItkImageType2D::New();
ItkImageType2D::IndexType startIndex;
startIndex[0] = 0; // first index on X
startIndex[1] = 0; // first index on Y
ItkImageType2D::SizeType size;
size[0] = this->m_ImageWidth; // size along X
size[1] = this->m_ImageHeight; // size along Y
ItkImageType2D::RegionType region;
region.SetSize( size );
region.SetIndex( startIndex );
itkInputImage->SetRegions( region );
itkInputImage->Allocate();
}
diff --git a/Modules/TubeGraph/include/mitkTubeGraphDataInteractor.h b/Modules/TubeGraph/include/mitkTubeGraphDataInteractor.h
index 8d07e4f3d8..622ed78781 100644
--- a/Modules/TubeGraph/include/mitkTubeGraphDataInteractor.h
+++ b/Modules/TubeGraph/include/mitkTubeGraphDataInteractor.h
@@ -1,122 +1,122 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkTubeGraphDataInteractor3D_h_
#define mitkTubeGraphDataInteractor3D_h_
#include <MitkTubeGraphExports.h>
#include <mitkBaseRenderer.h>
#include <mitkDataInteractor.h>
#include "mitkTubeGraph.h"
#include "mitkTubeGraphProperty.h"
namespace mitk
{
// Define events for TubeGraph interaction notifications
- itkEventMacro(SelectionChangedTubeGraphEvent, itk::AnyEvent);
+ itkEventMacroDeclaration(SelectionChangedTubeGraphEvent, itk::AnyEvent);
/**
* \brief
*
* \ingroup Interaction
*/
// Inherit from DataInteratcor, this provides functionality of a state machine and configurable inputs.
class MITKTUBEGRAPH_EXPORT TubeGraphDataInteractor : public DataInteractor
{
public:
mitkClassMacro(TubeGraphDataInteractor, DataInteractor);
itkNewMacro(Self);
/**
* Describes, which activation modes are available based on the
* currently picked tube:
*
* \li <b>None</b> means "no tube is active"
* \li <b>Single</b> means "only the picked tube is active"
* \li <b>ToRoot</b> means "all tubes from the picked on down to the root of the tube graph are active"
* \li <b>ToPeriphery</b> means "all tubes included in the subgraph of the currently picked vessel are active"
* \li <b>Points</b> means "shortes path between two picked tubes are active"
* \li <b>Multiple</b> means "all picked tubes are active"
*/
enum ActivationMode
{
None = 0,
Single,
ToRoot,
ToPeriphery,
Points,
Multiple
};
enum ActionMode
{
AttributationMode = 0,
AnnotationMode,
EditMode,
RootMode,
InformationMode
};
void SetActivationMode(const ActivationMode &activationMode);
ActivationMode GetActivationMode();
void SetActionMode(const ActionMode &actionMode);
ActionMode GetActionMode();
void ResetPickedTubes();
mitk::Point3D GetLastPickedPosition();
protected:
TubeGraphDataInteractor();
~TubeGraphDataInteractor() override;
/**
* Here actions strings from the loaded state machine pattern are mapped to functions of
* the DataInteractor. These functions are called when an action from the state machine pattern is executed.
*/
void ConnectActionsAndFunctions() override;
/**
* This function is called when a DataNode has been set/changed.
*/
void DataNodeChanged() override;
/**
* Initializes the movement, stores starting position.
*/
virtual bool CheckOverTube(const InteractionEvent *);
virtual void SelectTube(StateMachineAction *, InteractionEvent *);
virtual void DeselectTube(StateMachineAction *, InteractionEvent *);
void SelectTubesByActivationModus();
void UpdateActivation();
private:
std::vector<TubeGraph::TubeDescriptorType> GetTubesToRoot();
std::vector<TubeGraph::TubeDescriptorType> GetTubesBetweenPoints();
std::vector<TubeGraph::TubeDescriptorType> GetPathToPeriphery();
std::vector<TubeGraph::TubeDescriptorType> GetPathBetweenTubes(const TubeGraph::TubeDescriptorType &start,
const TubeGraph::TubeDescriptorType &end);
TubeGraph::Pointer m_TubeGraph;
TubeGraphProperty::Pointer m_TubeGraphProperty;
TubeGraph::TubeDescriptorType m_LastPickedTube;
TubeGraph::TubeDescriptorType m_SecondLastPickedTube;
ActivationMode m_ActivationMode;
ActionMode m_ActionMode;
mitk::TubeElement *m_LastPickedElement = nullptr;
};
}
#endif
diff --git a/Modules/TubeGraph/src/Interactions/mitkTubeGraphDataInteractor.cpp b/Modules/TubeGraph/src/Interactions/mitkTubeGraphDataInteractor.cpp
index 55cab1dbcb..bd0af564dd 100644
--- a/Modules/TubeGraph/src/Interactions/mitkTubeGraphDataInteractor.cpp
+++ b/Modules/TubeGraph/src/Interactions/mitkTubeGraphDataInteractor.cpp
@@ -1,285 +1,290 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkTubeGraphDataInteractor.h"
#include <mitkInteractionConst.h>
#include <mitkInteractionPositionEvent.h>
#include <mitkStatusBar.h>
#include "mitkTubeGraphPicker.h"
#include <vtkCamera.h>
#include <vtkInteractorStyle.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
#include <vtkRenderWindowInteractor.h>
+namespace mitk
+{
+ itkEventMacroDefinition(SelectionChangedTubeGraphEvent, itk::AnyEvent);
+}
+
mitk::TubeGraphDataInteractor::TubeGraphDataInteractor()
: m_LastPickedTube(TubeGraph::ErrorId),
m_SecondLastPickedTube(TubeGraph::ErrorId),
m_ActivationMode(None),
m_ActionMode(AttributationMode)
{
}
mitk::TubeGraphDataInteractor::~TubeGraphDataInteractor() {}
void mitk::TubeGraphDataInteractor::ConnectActionsAndFunctions()
{
// **Conditions** that can be used in the state machine, to ensure that certain conditions are met, before actually
// executing an action
CONNECT_CONDITION("isOverTube", CheckOverTube);
// **Function** in the statmachine patterns also referred to as **Actions**
CONNECT_FUNCTION("selectTube", SelectTube);
CONNECT_FUNCTION("deselectTube", DeselectTube);
}
void mitk::TubeGraphDataInteractor::DataNodeChanged()
{
if (GetDataNode() != nullptr)
{
if (GetDataNode()->GetData() != nullptr)
{
m_TubeGraph = dynamic_cast<TubeGraph *>(GetDataNode()->GetData());
m_TubeGraphProperty = dynamic_cast<TubeGraphProperty *>(
m_TubeGraph->GetProperty("Tube Graph.Visualization Information").GetPointer());
if (m_TubeGraphProperty.IsNull())
MITK_ERROR << "Something went wrong! No tube graph property!";
}
else
m_TubeGraph = nullptr;
}
else
m_TubeGraph = nullptr;
}
bool mitk::TubeGraphDataInteractor::CheckOverTube(const InteractionEvent *interactionEvent)
{
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return false;
auto *picker = new mitk::TubeGraphPicker();
picker->SetTubeGraph(m_TubeGraph);
auto pickedTube = picker->GetPickedTube(positionEvent->GetPositionInWorld());
TubeGraph::TubeDescriptorType tubeDescriptor = pickedTube.first;
if (tubeDescriptor != TubeGraph::ErrorId)
{
m_LastPickedElement = pickedTube.second;
m_SecondLastPickedTube = m_LastPickedTube;
m_LastPickedTube = tubeDescriptor;
return true;
}
else // nothing picked
return false;
}
void mitk::TubeGraphDataInteractor::SelectTube(StateMachineAction *, InteractionEvent *)
{
if (m_TubeGraph.IsNull())
return;
this->SelectTubesByActivationModus();
RenderingManager::GetInstance()->RequestUpdateAll();
if (m_ActivationMode != None)
{
// show tube id on status bar
std::stringstream displayText;
displayText << "Picked tube: ID [" << m_LastPickedTube.first << "," << m_LastPickedTube.second << "]";
StatusBar::GetInstance()->DisplayText(displayText.str().c_str());
// TODO!!! this->InvokeEvent(SelectionChangedTubeGraphEvent());
}
}
void mitk::TubeGraphDataInteractor::DeselectTube(StateMachineAction *, InteractionEvent *)
{
if (m_TubeGraph.IsNull())
return;
if ((m_ActivationMode != Multiple) && (m_ActivationMode != Points))
{
m_TubeGraphProperty->DeactivateAllTubes();
RenderingManager::GetInstance()->RequestUpdateAll();
// TODO!!!this->InvokeEvent(SelectionChangedTubeGraphEvent());
}
// show info on status bar
StatusBar::GetInstance()->DisplayText("No tube hit!");
}
void mitk::TubeGraphDataInteractor::SetActivationMode(const ActivationMode &activationMode)
{
m_ActivationMode = activationMode;
if (m_TubeGraph.IsNotNull())
if (m_LastPickedTube != mitk::TubeGraph::ErrorId)
this->UpdateActivation();
}
mitk::TubeGraphDataInteractor::ActivationMode mitk::TubeGraphDataInteractor::GetActivationMode()
{
return m_ActivationMode;
}
void mitk::TubeGraphDataInteractor::SetActionMode(const ActionMode &actionMode)
{
m_ActionMode = actionMode;
}
mitk::TubeGraphDataInteractor::ActionMode mitk::TubeGraphDataInteractor::GetActionMode()
{
return m_ActionMode;
}
void mitk::TubeGraphDataInteractor::SelectTubesByActivationModus()
{
if (m_LastPickedTube != mitk::TubeGraph::ErrorId)
{
this->UpdateActivation();
}
}
void mitk::TubeGraphDataInteractor::UpdateActivation()
{
if (m_ActionMode == RootMode)
{
m_TubeGraphProperty->DeactivateAllTubes();
m_TubeGraphProperty->SetTubeActive(m_LastPickedTube, true);
// QmitkTubeGraphSelectRootDialog* dialog = new QmitkTubeGraphSelectRootDialog(m_Parent);
// int dialogReturnValue = dialog->exec();
// delete dialog;
// if ( dialogReturnValue != QDialog::Rejected ) // user doesn't clicked cancel or pressed Esc or something similar
//{
m_TubeGraph->SetRootTube(m_LastPickedTube);
//}
m_TubeGraphProperty->DeactivateAllTubes();
RenderingManager::GetInstance()->RequestUpdateAll();
}
else
{
switch (m_ActivationMode)
{
case None:
{
m_TubeGraphProperty->DeactivateAllTubes();
}
break;
case Single:
{
m_TubeGraphProperty->DeactivateAllTubes();
m_TubeGraphProperty->SetTubeActive(m_LastPickedTube, true);
}
break;
case Multiple:
{ // special deactivation for multiple modus
// if activated--> deactivate; if not activated--> activate
if (m_TubeGraphProperty->IsTubeActive(m_LastPickedTube))
m_TubeGraphProperty->SetTubeActive(m_LastPickedTube, false);
else
m_TubeGraphProperty->SetTubeActive(m_LastPickedTube, true);
}
break;
case ToRoot:
{
m_TubeGraphProperty->DeactivateAllTubes();
std::vector<TubeGraph::TubeDescriptorType> activeTubes = this->GetTubesToRoot();
m_TubeGraphProperty->SetTubesActive(activeTubes);
}
break;
case ToPeriphery:
{
m_TubeGraphProperty->DeactivateAllTubes();
std::vector<TubeGraph::TubeDescriptorType> activeTubes = this->GetPathToPeriphery();
m_TubeGraphProperty->SetTubesActive(activeTubes);
}
break;
case Points:
{
m_TubeGraphProperty->DeactivateAllTubes();
std::vector<TubeGraph::TubeDescriptorType> activeTubes = this->GetTubesBetweenPoints();
m_TubeGraphProperty->SetTubesActive(activeTubes);
}
break;
default:
MITK_WARN << "Unknown tube graph interaction mode!";
break;
}
}
}
std::vector<mitk::TubeGraph::TubeDescriptorType> mitk::TubeGraphDataInteractor::GetTubesToRoot()
{
TubeGraph::TubeDescriptorType root = m_TubeGraph->GetRootTube();
if (root == TubeGraph::ErrorId)
{
root = m_TubeGraph->GetThickestTube();
m_TubeGraph->SetRootTube(root);
}
return this->GetPathBetweenTubes(m_LastPickedTube, root);
}
std::vector<mitk::TubeGraph::TubeDescriptorType> mitk::TubeGraphDataInteractor::GetTubesBetweenPoints()
{
return this->GetPathBetweenTubes(m_LastPickedTube, m_SecondLastPickedTube);
}
std::vector<mitk::TubeGraph::TubeDescriptorType> mitk::TubeGraphDataInteractor::GetPathBetweenTubes(
const mitk::TubeGraph::TubeDescriptorType &start, const mitk::TubeGraph::TubeDescriptorType &end)
{
std::vector<mitk::TubeGraph::TubeDescriptorType> solutionPath;
if ((start != TubeGraph::ErrorId) && (end != TubeGraph::ErrorId))
{
if (start != end)
solutionPath = m_TubeGraph->SearchAllPathBetweenVertices(start, end);
else
solutionPath.push_back(start);
}
return solutionPath;
}
std::vector<mitk::TubeGraph::TubeDescriptorType> mitk::TubeGraphDataInteractor::GetPathToPeriphery()
{
std::vector<mitk::TubeGraph::TubeDescriptorType> solutionPath;
if (m_LastPickedTube != TubeGraph::ErrorId)
solutionPath = m_TubeGraph->SearchPathToPeriphery(m_LastPickedTube);
return solutionPath;
}
void mitk::TubeGraphDataInteractor::ResetPickedTubes()
{
m_LastPickedTube = TubeGraph::ErrorId;
m_SecondLastPickedTube = TubeGraph::ErrorId;
}
mitk::Point3D mitk::TubeGraphDataInteractor::GetLastPickedPosition()
{
if (m_LastPickedElement)
return m_LastPickedElement->GetCoordinates();
else
return mitk::Point3D();
}
diff --git a/Modules/US/USFilters/mitkUSImageSource.cpp b/Modules/US/USFilters/mitkUSImageSource.cpp
index 2091d3733a..411a8bd76e 100644
--- a/Modules/US/USFilters/mitkUSImageSource.cpp
+++ b/Modules/US/USFilters/mitkUSImageSource.cpp
@@ -1,122 +1,121 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkUSImageSource.h"
#include "mitkProperties.h"
const char* mitk::USImageSource::IMAGE_PROPERTY_IDENTIFIER = "id_nummer";
mitk::USImageSource::USImageSource()
: m_OpenCVToMitkFilter(mitk::OpenCVToMitkImageFilter::New()),
m_MitkToOpenCVFilter(nullptr),
m_ImageFilter(mitk::BasicCombinationOpenCVImageFilter::New()),
- m_CurrentImageId(0),
- m_ImageFilterMutex(itk::FastMutexLock::New())
+ m_CurrentImageId(0)
{
}
mitk::USImageSource::~USImageSource()
{
}
void mitk::USImageSource::PushFilter(AbstractOpenCVImageFilter::Pointer filter)
{
m_ImageFilter->PushFilter(filter);
}
bool mitk::USImageSource::RemoveFilter(AbstractOpenCVImageFilter::Pointer filter)
{
return m_ImageFilter->RemoveFilter(filter);
}
bool mitk::USImageSource::GetIsFilterInThePipeline(AbstractOpenCVImageFilter::Pointer filter)
{
return m_ImageFilter->GetIsFilterOnTheList(filter);
}
std::vector<mitk::Image::Pointer> mitk::USImageSource::GetNextImage()
{
std::vector<mitk::Image::Pointer> result;
// Apply OpenCV based filters beforehand
if (m_ImageFilter.IsNotNull() && !m_ImageFilter->GetIsEmpty())
{
std::vector<cv::Mat> imageVector;
GetNextRawImage(imageVector);
if(result.size() != imageVector.size())
result.resize(imageVector.size());
for (size_t i = 0; i < imageVector.size(); ++i)
{
if (!imageVector[i].empty())
{
- m_ImageFilterMutex->Lock();
+ m_ImageFilterMutex.lock();
m_ImageFilter->FilterImage(imageVector[i], m_CurrentImageId);
- m_ImageFilterMutex->Unlock();
+ m_ImageFilterMutex.unlock();
// convert to MITK image
this->m_OpenCVToMitkFilter->SetOpenCVMat(imageVector[i]);
this->m_OpenCVToMitkFilter->Update();
// OpenCVToMitkImageFilter returns a standard mitk::image.
result[i] = this->m_OpenCVToMitkFilter->GetOutput();
}
}
}
else
{
this->GetNextRawImage(result);
}
for (size_t i = 0; i < result.size(); ++i)
{
if (result[i].IsNotNull())
{
result[i]->SetProperty(IMAGE_PROPERTY_IDENTIFIER, mitk::IntProperty::New(m_CurrentImageId));
}
else
{
//MITK_WARN("mitkUSImageSource") << "Result image " << i << " is not set.";
result[i] = mitk::Image::New();
}
}
m_CurrentImageId++;
return result;
}
void mitk::USImageSource::GetNextRawImage(std::vector<cv::Mat>& imageVector)
{
// create filter object if it does not exist yet
if (!m_MitkToOpenCVFilter)
{
m_MitkToOpenCVFilter = mitk::ImageToOpenCVImageFilter::New();
}
// get mitk image through virtual method of the subclass
std::vector<mitk::Image::Pointer> mitkImg;
this->GetNextRawImage(mitkImg);
for (unsigned int i = 0; i < mitkImg.size(); ++i)
{
if (mitkImg[i].IsNull() || !mitkImg[i]->IsInitialized())
{
imageVector[i] = cv::Mat();
}
else
{
// convert mitk::Image to an OpenCV image
m_MitkToOpenCVFilter->SetImage(mitkImg[i]);
imageVector[i] = m_MitkToOpenCVFilter->GetOpenCVMat();
}
}
}
diff --git a/Modules/US/USFilters/mitkUSImageSource.h b/Modules/US/USFilters/mitkUSImageSource.h
index 7c66c5e2e4..c11147f1b2 100644
--- a/Modules/US/USFilters/mitkUSImageSource.h
+++ b/Modules/US/USFilters/mitkUSImageSource.h
@@ -1,98 +1,99 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKUSImageSource_H_HEADER_INCLUDED_
#define MITKUSImageSource_H_HEADER_INCLUDED_
+#include <mutex>
+
// ITK
#include <itkProcessObject.h>
-#include <itkFastMutexLock.h>
// MITK
#include <MitkUSExports.h>
#include <mitkCommon.h>
#include "mitkBasicCombinationOpenCVImageFilter.h"
#include "mitkOpenCVToMitkImageFilter.h"
#include "mitkImageToOpenCVImageFilter.h"
namespace mitk {
/**
* \brief This is an abstract superclass for delivering USImages.
* Each subclass must implement the method mitk::USImageSource::GetNextRawImage().
* The public method mitk::USImageSource::GetNextImage() can the be used to
* get the next image from the image source. This image will be filtered by
* the filter set with mitk::USImageSource::SetImageFilter().
*
* \ingroup US
*/
class MITKUS_EXPORT USImageSource : public itk::Object
{
public:
static const char* IMAGE_PROPERTY_IDENTIFIER;
mitkClassMacroItkParent(USImageSource, itk::Object);
itkGetMacro(ImageFilter, mitk::BasicCombinationOpenCVImageFilter::Pointer);
void PushFilter(AbstractOpenCVImageFilter::Pointer filter);
bool RemoveFilter(AbstractOpenCVImageFilter::Pointer filter);
bool GetIsFilterInThePipeline(AbstractOpenCVImageFilter::Pointer filter);
/**
* \brief Retrieves the next frame. This will typically be the next frame
* in a file or the last cached file in a device. The image is filtered if
* a filter was set by mitk::USImageSource::SetImageFilter().
*
* \return pointer to the next USImage (filtered if set)
*/
std::vector<mitk::Image::Pointer> GetNextImage();
protected:
USImageSource();
~USImageSource() override;
/**
* \brief Set the given OpenCV image matrix to the next image received
* from the device or file.
*
* The standard implementation calls the overloaded function with an
* mitk::Image and converts this image to OpenCV then. One should reimplement
* this method for a better performance if an image filter is set.
*/
virtual void GetNextRawImage(std::vector<cv::Mat>&);
/**
* \brief Set mitk::Image to the next image received from the device or file.
* This method must be implemented in every subclass.
*/
virtual void GetNextRawImage(std::vector<mitk::Image::Pointer>&) = 0;
/**
* \brief Used to convert from OpenCV Images to MITK Images.
*/
mitk::OpenCVToMitkImageFilter::Pointer m_OpenCVToMitkFilter;
/**
* \brief Used to convert from MITK Images to OpenCV Images.
*/
mitk::ImageToOpenCVImageFilter::Pointer m_MitkToOpenCVFilter;
private:
/**
* \brief Filter is executed during mitk::USImageVideoSource::GetNextImage().
*/
BasicCombinationOpenCVImageFilter::Pointer m_ImageFilter;
int m_CurrentImageId;
- itk::FastMutexLock::Pointer m_ImageFilterMutex;
+ std::mutex m_ImageFilterMutex;
};
} // namespace mitk
#endif /* MITKUSImageSource_H_HEADER_INCLUDED_ */
diff --git a/Modules/US/USFilters/mitkUSImageVideoSource.cpp b/Modules/US/USFilters/mitkUSImageVideoSource.cpp
index 14892f9d38..539158691c 100644
--- a/Modules/US/USFilters/mitkUSImageVideoSource.cpp
+++ b/Modules/US/USFilters/mitkUSImageVideoSource.cpp
@@ -1,226 +1,226 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// MITK HEADER
#include "mitkUSImageVideoSource.h"
#include "mitkImage.h"
//Other
#include <cstdio>
#include <highgui.h>
mitk::USImageVideoSource::USImageVideoSource()
: m_VideoCapture(new cv::VideoCapture()),
m_IsVideoReady(false),
m_IsGreyscale(false),
m_IsCropped(false),
m_ResolutionOverrideWidth(0),
m_ResolutionOverrideHeight(0),
m_ResolutionOverride(false),
m_GrayscaleFilter(mitk::ConvertGrayscaleOpenCVImageFilter::New()),
m_CropFilter(mitk::CropOpenCVImageFilter::New())
{
}
mitk::USImageVideoSource::~USImageVideoSource()
{
m_VideoCapture->release();
delete m_VideoCapture;
}
void mitk::USImageVideoSource::SetVideoFileInput(std::string path)
{
m_VideoCapture->open(path.c_str());
// check if we succeeded
if(!m_VideoCapture->isOpened()) { m_IsVideoReady = false; }
else { m_IsVideoReady = true; }
// if Override is enabled, use it
if (m_ResolutionOverride)
{
m_VideoCapture->set(CV_CAP_PROP_FRAME_WIDTH, this->m_ResolutionOverrideWidth);
m_VideoCapture->set(CV_CAP_PROP_FRAME_HEIGHT, this->m_ResolutionOverrideHeight);
}
}
void mitk::USImageVideoSource::SetCameraInput(int deviceID)
{
m_VideoCapture->open(deviceID);
if(!m_VideoCapture->isOpened()) // check if we succeeded
m_IsVideoReady = false;
else
m_IsVideoReady = true;
// if Override is enabled, use it
if (m_ResolutionOverride)
{
m_VideoCapture->set(CV_CAP_PROP_FRAME_WIDTH, this->m_ResolutionOverrideWidth);
m_VideoCapture->set(CV_CAP_PROP_FRAME_HEIGHT, this->m_ResolutionOverrideHeight);
}
}
void mitk::USImageVideoSource::ReleaseInput()
{
m_VideoCapture->release();
delete m_VideoCapture;
m_VideoCapture = new cv::VideoCapture();
}
void mitk::USImageVideoSource::SetColorOutput(bool isColor){
if ( ! isColor && ! m_IsGreyscale )
{
this->PushFilter(m_GrayscaleFilter.GetPointer());
}
else if ( isColor && m_IsGreyscale )
{
this->RemoveFilter(m_GrayscaleFilter.GetPointer());
}
m_IsGreyscale = !isColor;
}
int mitk::USImageVideoSource::GetImageHeight()
{
if (m_VideoCapture) { return m_VideoCapture->get(CV_CAP_PROP_FRAME_HEIGHT); }
else { return 0; }
}
int mitk::USImageVideoSource::GetImageWidth()
{
if (m_VideoCapture) { return m_VideoCapture->get(CV_CAP_PROP_FRAME_WIDTH); }
else { return 0; }
}
bool mitk::USImageVideoSource::GetIsReady()
{
if (!m_VideoCapture) { return false; }
return m_VideoCapture->isOpened();
}
void mitk::USImageVideoSource::SetRegionOfInterest(int topLeftX, int topLeftY, int bottomRightX, int bottomRightY)
{
m_CropFilter->SetCropRegion(topLeftX, topLeftY, bottomRightX, bottomRightY);
if (! m_IsCropped && ! m_CropFilter->GetIsCropRegionEmpty())
{
this->PushFilter(m_CropFilter.GetPointer());
m_IsCropped = true;
}
}
void mitk::USImageVideoSource::SetRegionOfInterest(USImageRoi roi)
{
this->SetRegionOfInterest(roi.topLeftX, roi.topLeftY, roi.bottomRightX, roi.bottomRightY);
}
void mitk::USImageVideoSource::SetCropping(USImageCropping cropping)
{
int width = this->GetImageWidth();
int height = this->GetImageHeight();
this->SetRegionOfInterest(cropping.left, cropping.top, width - cropping.right, height - cropping.bottom);
}
mitk::USImageVideoSource::USImageCropping mitk::USImageVideoSource::GetCropping()
{
cv::Rect cropRect = m_CropFilter->GetCropRegion();
USImageCropping cropping;
cropping.left = cropRect.x;
cropping.top = cropRect.y;
if ( cropRect.height == 0 )
{
cropping.bottom = 0;
}
else
{
cropping.bottom = this->GetImageHeight() - (cropRect.y + cropRect.height);
}
if ( cropRect.width == 0 )
{
cropping.right = 0;
}
else
{
cropping.right = this->GetImageWidth() - (cropRect.x + cropRect.width);
}
return cropping;
}
mitk::USImageVideoSource::USImageRoi mitk::USImageVideoSource::GetRegionOfInterest()
{
cv::Rect cropRect = m_CropFilter->GetCropRegion();
return USImageRoi(cropRect.x, cropRect.y, cropRect.x + cropRect.width, cropRect.y + cropRect.height);
}
void mitk::USImageVideoSource::RemoveRegionOfInterest()
{
this->RemoveFilter(m_CropFilter.GetPointer());
m_IsCropped = false;
}
void mitk::USImageVideoSource::GetNextRawImage(std::vector<cv::Mat>& image )
{
// loop video if necessary
//Commented out because setting and getting of these properties is not supported. Therefore on Linux
//you'll always get some Highgui errors from OpenCV
/*if (m_VideoCapture->get(CV_CAP_PROP_POS_FRAMES) == m_VideoCapture->get(CV_CAP_PROP_FRAME_COUNT))
{
m_VideoCapture->set(CV_CAP_PROP_POS_FRAMES, 0);
}*/
if (image.size() != 1)
image.resize(1);
// retrieve image
*m_VideoCapture >> image[0]; // get a new frame from camera
}
void mitk::USImageVideoSource::GetNextRawImage(std::vector<mitk::Image::Pointer>& image )
{
if (image.size() != 1)
image.resize(1);
std::vector<cv::Mat> cv_img;
this->GetNextRawImage(cv_img);
// convert to MITK-Image
- IplImage ipl_img = cv_img[0];
+ IplImage ipl_img = cvIplImage(cv_img[0]);
this->m_OpenCVToMitkFilter->SetOpenCVImage(&ipl_img);
this->m_OpenCVToMitkFilter->Update();
// OpenCVToMitkImageFilter returns a standard mitk::image. We then transform it into an USImage
image[0] = this->m_OpenCVToMitkFilter->GetOutput();
// clean up
cv_img[0].release();
}
void mitk::USImageVideoSource::OverrideResolution(int width, int height)
{
this->m_ResolutionOverrideHeight = height;
this->m_ResolutionOverrideWidth = width;
if (m_VideoCapture != nullptr)
{
m_VideoCapture->set(CV_CAP_PROP_FRAME_WIDTH, width);
m_VideoCapture->set(CV_CAP_PROP_FRAME_HEIGHT, height);
}
}
diff --git a/Modules/US/USModel/mitkUSDevice.cpp b/Modules/US/USModel/mitkUSDevice.cpp
index 1f4ef2a873..a88bcecb6a 100644
--- a/Modules/US/USModel/mitkUSDevice.cpp
+++ b/Modules/US/USModel/mitkUSDevice.cpp
@@ -1,713 +1,683 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkUSDevice.h"
#include "mitkImageReadAccessor.h"
// US Control Interfaces
#include "mitkUSControlInterfaceProbes.h"
#include "mitkUSControlInterfaceBMode.h"
#include "mitkUSControlInterfaceDoppler.h"
// Microservices
#include <usGetModuleContext.h>
#include <usModule.h>
#include <usServiceProperties.h>
#include <usModuleContext.h>
mitk::USDevice::PropertyKeys mitk::USDevice::GetPropertyKeys()
{
static mitk::USDevice::PropertyKeys propertyKeys;
return propertyKeys;
}
mitk::USDevice::USImageCropArea mitk::USDevice::GetCropArea()
{
MITK_INFO << "Return Crop Area L:" << m_CropArea.cropLeft
<< " R:" << m_CropArea.cropRight << " T:" << m_CropArea.cropTop
<< " B:" << m_CropArea.cropBottom;
return m_CropArea;
}
unsigned int mitk::USDevice::GetSizeOfImageVector()
{
return m_ImageVector.size();
}
mitk::USDevice::USDevice(std::string manufacturer, std::string model)
: mitk::ImageSource(),
- m_FreezeBarrier(nullptr),
- m_FreezeMutex(),
- m_MultiThreader(itk::MultiThreader::New()),
- m_ImageMutex(itk::FastMutexLock::New()),
- m_ThreadID(-1),
m_ImageVector(),
m_Spacing(),
m_IGTLServer(nullptr),
m_IGTLMessageProvider(nullptr),
m_ImageToIGTLMsgFilter(nullptr),
m_IsFreezed(false),
m_DeviceState(State_NoState),
m_NumberOfOutputs(1),
m_ServiceProperties(),
m_ServiceRegistration(),
m_Manufacturer(manufacturer),
m_Name(model),
m_Comment(),
m_SpawnAcquireThread(true),
m_UnregisteringStarted(false)
{
USImageCropArea empty;
empty.cropBottom = 0;
empty.cropTop = 0;
empty.cropLeft = 0;
empty.cropRight = 0;
this->m_CropArea = empty;
// set number of outputs
this->SetNumberOfIndexedOutputs(m_NumberOfOutputs);
// create a new output
mitk::Image::Pointer newOutput = mitk::Image::New();
this->SetNthOutput(0, newOutput);
}
mitk::USDevice::USDevice(mitk::USImageMetadata::Pointer metadata)
: mitk::ImageSource(),
- m_FreezeBarrier(nullptr),
- m_FreezeMutex(),
- m_MultiThreader(itk::MultiThreader::New()),
- m_ImageMutex(itk::FastMutexLock::New()),
- m_ThreadID(-1),
m_ImageVector(),
m_Spacing(),
m_IGTLServer(nullptr),
m_IGTLMessageProvider(nullptr),
m_ImageToIGTLMsgFilter(nullptr),
m_IsFreezed(false),
m_DeviceState(State_NoState),
m_NumberOfOutputs(1),
m_ServiceProperties(),
m_ServiceRegistration(),
m_SpawnAcquireThread(true),
m_UnregisteringStarted(false)
{
m_Manufacturer = metadata->GetDeviceManufacturer();
m_Name = metadata->GetDeviceModel();
m_Comment = metadata->GetDeviceComment();
USImageCropArea empty;
empty.cropBottom = 0;
empty.cropTop = 0;
empty.cropLeft = 0;
empty.cropRight = 0;
this->m_CropArea = empty;
// set number of outputs
this->SetNumberOfIndexedOutputs(m_NumberOfOutputs);
// create a new output
mitk::Image::Pointer newOutput = mitk::Image::New();
this->SetNthOutput(0, newOutput);
}
mitk::USDevice::~USDevice()
{
- if (m_ThreadID >= 0)
- {
- m_MultiThreader->TerminateThread(m_ThreadID);
- }
+ if (m_Thread.joinable())
+ m_Thread.detach();
// make sure that the us device is not registered at the micro service
// anymore after it is destructed
this->UnregisterOnService();
}
mitk::USAbstractControlInterface::Pointer
mitk::USDevice::GetControlInterfaceCustom()
{
MITK_INFO << "Custom control interface does not exist for this object.";
return nullptr;
}
mitk::USControlInterfaceBMode::Pointer
mitk::USDevice::GetControlInterfaceBMode()
{
MITK_INFO << "Control interface BMode does not exist for this object.";
return nullptr;
}
mitk::USControlInterfaceProbes::Pointer
mitk::USDevice::GetControlInterfaceProbes()
{
MITK_INFO << "Control interface Probes does not exist for this object.";
return nullptr;
}
mitk::USControlInterfaceDoppler::Pointer
mitk::USDevice::GetControlInterfaceDoppler()
{
MITK_INFO << "Control interface Doppler does not exist for this object.";
return nullptr;
}
void mitk::USDevice::SetManufacturer(std::string manufacturer)
{
m_Manufacturer = manufacturer;
if (m_DeviceState >= State_Initialized)
{
this->UpdateServiceProperty(
mitk::USDevice::GetPropertyKeys().US_PROPKEY_MANUFACTURER,
manufacturer);
}
}
void mitk::USDevice::SetName(std::string name)
{
m_Name = name;
if (m_DeviceState >= State_Initialized)
{
this->UpdateServiceProperty(
mitk::USDevice::GetPropertyKeys().US_PROPKEY_NAME, name);
}
}
void mitk::USDevice::SetComment(std::string comment)
{
m_Comment = comment;
if (m_DeviceState >= State_Initialized)
{
this->UpdateServiceProperty(
mitk::USDevice::GetPropertyKeys().US_PROPKEY_COMMENT, comment);
}
}
us::ServiceProperties mitk::USDevice::ConstructServiceProperties()
{
mitk::USDevice::PropertyKeys propertyKeys = mitk::USDevice::GetPropertyKeys();
us::ServiceProperties props;
props[propertyKeys.US_PROPKEY_ISCONNECTED] =
this->GetIsConnected() ? "true" : "false";
props[propertyKeys.US_PROPKEY_ISACTIVE] =
this->GetIsActive() ? "true" : "false";
props[propertyKeys.US_PROPKEY_LABEL] = this->GetServicePropertyLabel();
// get identifier of selected probe if there is one selected
mitk::USControlInterfaceProbes::Pointer probesControls =
this->GetControlInterfaceProbes();
if (probesControls.IsNotNull() && probesControls->GetIsActive())
{
mitk::USProbe::Pointer probe = probesControls->GetSelectedProbe();
if (probe.IsNotNull())
{
props[propertyKeys.US_PROPKEY_PROBES_SELECTED] = probe->GetName();
}
}
props[propertyKeys.US_PROPKEY_CLASS] = GetDeviceClass();
props[propertyKeys.US_PROPKEY_MANUFACTURER] = m_Manufacturer;
props[propertyKeys.US_PROPKEY_NAME] = m_Name;
props[propertyKeys.US_PROPKEY_COMMENT] = m_Comment;
m_ServiceProperties = props;
return props;
}
void mitk::USDevice::UnregisterOnService()
{
// unregister on micro service
if (m_ServiceRegistration && !m_UnregisteringStarted)
{
// make sure that unregister is not started a second
// time due to a callback during unregister for example
m_UnregisteringStarted = true;
m_ServiceRegistration.Unregister();
m_ServiceRegistration = 0;
}
}
bool mitk::USDevice::Initialize()
{
if (!this->OnInitialization())
{
return false;
}
m_DeviceState = State_Initialized;
// Get Context and Module
us::ModuleContext* context = us::GetModuleContext();
us::ServiceProperties props = this->ConstructServiceProperties();
m_ServiceRegistration = context->RegisterService(this, props);
return true;
}
bool mitk::USDevice::Connect()
{
MITK_DEBUG << "mitk::USDevice::Connect() called";
if (this->GetIsConnected())
{
MITK_INFO("mitkUSDevice") << "Tried to connect an ultrasound device that "
"was already connected. Ignoring call...";
return true;
}
if (!this->GetIsInitialized())
{
MITK_ERROR("mitkUSDevice")
<< "Cannot connect device if it is not in initialized state.";
return false;
}
// Prepare connection, fail if this fails.
if (!this->OnConnection())
{
return false;
}
// Update state
m_DeviceState = State_Connected;
this->UpdateServiceProperty(
mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISCONNECTED, true);
return true;
}
void mitk::USDevice::ConnectAsynchron()
{
- this->m_MultiThreader->SpawnThread(this->ConnectThread, this);
+ m_Thread = std::thread(&USDevice::ConnectThread, this);
}
bool mitk::USDevice::Disconnect()
{
if (!GetIsConnected())
{
MITK_WARN << "Tried to disconnect an ultrasound device that was not "
"connected. Ignoring call...";
return false;
}
// Prepare connection, fail if this fails.
if (!this->OnDisconnection())
return false;
// Update state
m_DeviceState = State_Initialized;
this->UpdateServiceProperty(
mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISCONNECTED, false);
return true;
}
bool mitk::USDevice::Activate()
{
if (!this->GetIsConnected())
{
MITK_INFO("mitkUSDevice")
<< "Cannot activate device if it is not in connected state.";
return true;
}
if (OnActivation())
{
m_DeviceState = State_Activated;
- m_FreezeBarrier = itk::ConditionVariable::New();
-
// spawn thread for aquire images if us device is active
if (m_SpawnAcquireThread)
{
- this->m_ThreadID =
- this->m_MultiThreader->SpawnThread(this->Acquire, this);
+ m_Thread = std::thread(&USDevice::Acquire, this);
}
this->UpdateServiceProperty(
mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISACTIVE, true);
this->UpdateServiceProperty(
mitk::USDevice::GetPropertyKeys().US_PROPKEY_LABEL,
this->GetServicePropertyLabel());
// initialize the b mode control properties of the micro service
mitk::USControlInterfaceBMode::Pointer bmodeControls =
this->GetControlInterfaceBMode();
if (bmodeControls.IsNotNull())
{
bmodeControls->Initialize();
}
}
this->ProvideViaOIGTL();
return m_DeviceState == State_Activated;
}
void mitk::USDevice::ProvideViaOIGTL()
{
// create a new OpenIGTLink Server
if (m_IGTLServer.IsNull())
m_IGTLServer = mitk::IGTLServer::New(true);
m_IGTLServer->SetName(this->GetName());
// create a new OpenIGTLink Device source
if (m_IGTLMessageProvider.IsNull())
m_IGTLMessageProvider = mitk::IGTLMessageProvider::New();
// set the OpenIGTLink server as the source for the device source
m_IGTLMessageProvider->SetIGTLDevice(m_IGTLServer);
// register the provider so that it can be configured with the IGTL manager
// plugin. This could be hardcoded but now I already have the fancy plugin.
m_IGTLMessageProvider->RegisterAsMicroservice();
m_ImageToIGTLMsgFilter = mitk::ImageToIGTLMessageFilter::New();
m_ImageToIGTLMsgFilter->ConnectTo(this);
// set the name of this filter to identify it easier
m_ImageToIGTLMsgFilter->SetName(this->GetName());
// register this filter as micro service. The message provider looks for
// provided IGTLMessageSources, once it found this microservice and someone
// requested this data type then the provider will connect with this filter
// automatically.
m_ImageToIGTLMsgFilter->RegisterAsMicroservice();
}
void mitk::USDevice::Deactivate()
{
if (!this->GetIsActive())
{
MITK_WARN("mitkUSDevice")
<< "Cannot deactivate a device which is not activae.";
return;
}
if (!OnDeactivation())
{
return;
}
DisableOIGTL();
m_DeviceState = State_Connected;
this->UpdateServiceProperty(
mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISACTIVE, false);
this->UpdateServiceProperty(
mitk::USDevice::GetPropertyKeys().US_PROPKEY_LABEL,
this->GetServicePropertyLabel());
}
void mitk::USDevice::DisableOIGTL()
{
// TODO: This seems not to be enough cleanup to catch all cases. For example, if the device is disconnected
// from the OIGTL GUI, this won't get cleaned up correctly.
m_IGTLServer->CloseConnection();
m_IGTLMessageProvider->UnRegisterMicroservice();
m_ImageToIGTLMsgFilter->UnRegisterMicroservice();
}
void mitk::USDevice::SetIsFreezed(bool freeze)
{
if (!this->GetIsActive())
{
MITK_WARN("mitkUSDevice")
<< "Cannot freeze or unfreeze if device is not active.";
return;
}
this->OnFreeze(freeze);
if (freeze)
{
+ std::lock_guard<std::mutex> lock(m_FreezeMutex);
m_IsFreezed = true;
}
else
{
- m_IsFreezed = false;
+ {
+ std::lock_guard<std::mutex> lock(m_FreezeMutex);
+ m_IsFreezed = false;
+ }
// wake up the image acquisition thread
- m_FreezeBarrier->Signal();
+ m_FreezeBarrier.notify_one();
}
}
bool mitk::USDevice::GetIsFreezed()
{
/*
if (!this->GetIsActive())
{
MITK_WARN("mitkUSDevice")("mitkUSTelemedDevice")
<< "Cannot get freeze state if the hardware interface is not ready. "
"Returning false...";
return false;
}*/
return m_IsFreezed;
}
void mitk::USDevice::PushFilter(AbstractOpenCVImageFilter::Pointer filter)
{
mitk::USImageSource::Pointer imageSource = this->GetUSImageSource();
if (imageSource.IsNull())
{
MITK_ERROR << "ImageSource must not be null when pushing a filter.";
mitkThrow() << "ImageSource must not be null when pushing a filter.";
}
imageSource->PushFilter(filter);
}
void mitk::USDevice::PushFilterIfNotPushedBefore(
AbstractOpenCVImageFilter::Pointer filter)
{
mitk::USImageSource::Pointer imageSource = this->GetUSImageSource();
if (imageSource.IsNull())
{
MITK_ERROR << "ImageSource must not be null when pushing a filter.";
mitkThrow() << "ImageSource must not be null when pushing a filter.";
}
if (!imageSource->GetIsFilterInThePipeline(filter))
{
imageSource->PushFilter(filter);
}
}
bool mitk::USDevice::RemoveFilter(AbstractOpenCVImageFilter::Pointer filter)
{
mitk::USImageSource::Pointer imageSource = this->GetUSImageSource();
if (imageSource.IsNull())
{
MITK_ERROR << "ImageSource must not be null when pushing a filter.";
mitkThrow() << "ImageSource must not be null when removing a filter.";
}
return imageSource->RemoveFilter(filter);
}
void mitk::USDevice::UpdateServiceProperty(std::string key, std::string value)
{
m_ServiceProperties[key] = value;
m_ServiceRegistration.SetProperties(m_ServiceProperties);
// send event to notify listeners about the changed property
m_PropertyChangedMessage(key, value);
}
void mitk::USDevice::UpdateServiceProperty(std::string key, double value)
{
std::stringstream stream;
stream << value;
this->UpdateServiceProperty(key, stream.str());
}
void mitk::USDevice::UpdateServiceProperty(std::string key, bool value)
{
this->UpdateServiceProperty(
key, value ? std::string("true") : std::string("false"));
}
/**
mitk::Image* mitk::USDevice::GetOutput()
{
if (this->GetNumberOfOutputs() < 1)
return nullptr;
return static_cast<USImage*>(this->ProcessObject::GetPrimaryOutput());
}
mitk::Image* mitk::USDevice::GetOutput(unsigned int idx)
{
if (this->GetNumberOfOutputs() < 1)
return nullptr;
return static_cast<USImage*>(this->ProcessObject::GetOutput(idx));
}
void mitk::USDevice::GraftOutput(itk::DataObject *graft)
{
this->GraftNthOutput(0, graft);
}
void mitk::USDevice::GraftNthOutput(unsigned int idx, itk::DataObject *graft)
{
if ( idx >= this->GetNumberOfOutputs() )
{
itkExceptionMacro(<<"Requested to graft output " << idx <<
" but this filter only has " << this->GetNumberOfOutputs() << " Outputs.");
}
if ( !graft )
{
itkExceptionMacro(<<"Requested to graft output with a nullptr pointer object" );
}
itk::DataObject* output = this->GetOutput(idx);
if ( !output )
{
itkExceptionMacro(<<"Requested to graft output that is a nullptr pointer" );
}
// Call Graft on USImage to copy member data
output->Graft( graft );
}
*/
void mitk::USDevice::GrabImage()
{
std::vector<mitk::Image::Pointer> image = this->GetUSImageSource()->GetNextImage();
- m_ImageMutex->Lock();
+ m_ImageMutex.lock();
this->SetImageVector(image);
- m_ImageMutex->Unlock();
+ m_ImageMutex.unlock();
}
//########### GETTER & SETTER ##################//
bool mitk::USDevice::GetIsInitialized()
{
return m_DeviceState == State_Initialized;
}
bool mitk::USDevice::GetIsActive() { return m_DeviceState == State_Activated; }
bool mitk::USDevice::GetIsConnected()
{
return m_DeviceState == State_Connected;
}
std::string mitk::USDevice::GetDeviceManufacturer() { return m_Manufacturer; }
std::string mitk::USDevice::GetDeviceModel() { return m_Name; }
std::string mitk::USDevice::GetDeviceComment() { return m_Comment; }
void mitk::USDevice::SetSpacing(double xSpacing, double ySpacing)
{
m_Spacing[0] = xSpacing;
m_Spacing[1] = ySpacing;
m_Spacing[2] = 1;
if( m_ImageVector.size() > 0 )
{
for( size_t index = 0; index < m_ImageVector.size(); ++index )
{
auto& image = m_ImageVector[index];
if( image.IsNotNull() && image->IsInitialized() )
{
image->GetGeometry()->SetSpacing(m_Spacing);
}
}
this->Modified();
}
MITK_INFO << "Spacing: " << m_Spacing;
}
void mitk::USDevice::GenerateData()
{
- m_ImageMutex->Lock();
+ m_ImageMutex.lock();
for (unsigned int i = 0; i < m_ImageVector.size() && i < this->GetNumberOfIndexedOutputs(); ++i)
{
auto& image = m_ImageVector[i];
if (image.IsNull() || !image->IsInitialized())
{
// skip image
}
else
{
mitk::Image::Pointer output = this->GetOutput(i);
if (!output->IsInitialized() ||
output->GetDimension(0) != image->GetDimension(0) ||
output->GetDimension(1) != image->GetDimension(1) ||
output->GetDimension(2) != image->GetDimension(2) ||
output->GetPixelType() != image->GetPixelType())
{
output->Initialize(image->GetPixelType(), image->GetDimension(),
image->GetDimensions());
}
// copy contents of the given image into the member variable
mitk::ImageReadAccessor inputReadAccessor(image);
output->SetImportVolume(inputReadAccessor.GetData());
output->SetGeometry(image->GetGeometry());
}
}
- m_ImageMutex->Unlock();
+ m_ImageMutex.unlock();
};
std::string mitk::USDevice::GetServicePropertyLabel()
{
std::string isActive;
if (this->GetIsActive())
{
isActive = " (Active)";
}
else
{
isActive = " (Inactive)";
}
// e.g.: Zonare MyLab5 (Active)
return m_Manufacturer + " " + m_Name + isActive;
}
-ITK_THREAD_RETURN_TYPE mitk::USDevice::Acquire(void* pInfoStruct)
+void mitk::USDevice::Acquire()
{
- /* extract this pointer from Thread Info structure */
- struct itk::MultiThreader::ThreadInfoStruct* pInfo =
- (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct;
- mitk::USDevice* device = (mitk::USDevice*)pInfo->UserData;
- while (device->GetIsActive())
+ while (this->GetIsActive())
{
// lock this thread when ultrasound device is freezed
- if (device->m_IsFreezed)
- {
- itk::SimpleMutexLock* mutex = &(device->m_FreezeMutex);
- mutex->Lock();
+ std::unique_lock<std::mutex> lock(m_FreezeMutex);
+ m_FreezeBarrier.wait(lock, [this] { return !m_IsFreezed; });
- if (device->m_FreezeBarrier.IsNotNull())
- {
- device->m_FreezeBarrier->Wait(mutex);
- }
- }
- device->GrabImage();
+ this->GrabImage();
}
- return ITK_THREAD_RETURN_VALUE;
}
-ITK_THREAD_RETURN_TYPE mitk::USDevice::ConnectThread(void* pInfoStruct)
+void mitk::USDevice::ConnectThread()
{
- /* extract this pointer from Thread Info structure */
- struct itk::MultiThreader::ThreadInfoStruct* pInfo =
- (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct;
- mitk::USDevice* device = (mitk::USDevice*)pInfo->UserData;
-
- device->Connect();
-
- return ITK_THREAD_RETURN_VALUE;
+ this->Connect();
}
void mitk::USDevice::ProbeChanged(std::string probename)
{
this->UpdateServiceProperty(mitk::USDevice::GetPropertyKeys().US_PROPKEY_PROBES_SELECTED, probename);
}
void mitk::USDevice::DepthChanged(double depth)
{
this->UpdateServiceProperty(mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DEPTH, depth);
}
diff --git a/Modules/US/USModel/mitkUSDevice.h b/Modules/US/USModel/mitkUSDevice.h
index 720016b004..122b61d828 100644
--- a/Modules/US/USModel/mitkUSDevice.h
+++ b/Modules/US/USModel/mitkUSDevice.h
@@ -1,531 +1,532 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef MITKUSDevice_H_HEADER_INCLUDED_
#define MITKUSDevice_H_HEADER_INCLUDED_
// STL
+#include <condition_variable>
+#include <mutex>
+#include <thread>
#include <vector>
// MitkUS
#include "mitkUSProbe.h"
#include <MitkUSExports.h>
#include "mitkUSImageSource.h"
// MitkIGTL
#include "mitkIGTLMessageProvider.h"
#include "mitkIGTLServer.h"
#include "mitkIGTLDeviceSource.h"
#include "mitkImageToIGTLMessageFilter.h"
// MITK
#include <mitkCommon.h>
#include <mitkMessage.h>
#include <mitkImageSource.h>
// ITK
#include <itkObjectFactory.h>
-#include <itkConditionVariable.h>
// Microservices
#include <mitkServiceInterface.h>
#include <usServiceRegistration.h>
#include <usServiceProperties.h>
// DEPRECATED
#include "mitkUSImageMetadata.h"
namespace itk {
template<class T> class SmartPointer;
}
namespace mitk {
class USAbstractControlInterface;
class USControlInterfaceBMode;
class USControlInterfaceProbes;
class USControlInterfaceDoppler;
/**
* \brief A device holds information about it's model, make and the connected probes. It is the
* common super class for all devices and acts as an image source for mitkUSImages. It is the base class
* for all US Devices, and every new device should extend it.
*
* US Devices support output of calibrated images, i.e. images that include a specific geometry.
* To achieve this, call SetCalibration, and make sure that the subclass also calls apply
* transformation at some point (The USDevice does not automatically apply the transformation to the image)
*
* Note that USDevices will be removed from micro servive when their
* destructor is called. Registering into micro service is done when
* mitk::USDevice::Initialize() is called.
*
* \ingroup US
*/
class MITKUS_EXPORT USDevice : public mitk::ImageSource
{
public:
enum DeviceStates { State_NoState, State_Initialized, State_Connected, State_Activated };
mitkClassMacro(USDevice, mitk::ImageSource);
itkSetMacro(SpawnAcquireThread, bool);
itkGetMacro(SpawnAcquireThread, bool);
struct USImageCropArea
{
int cropLeft;
int cropRight;
int cropBottom;
int cropTop;
};
/**
* \brief These constants are used in conjunction with Microservices.
* The constants aren't defined as static member attributes to avoid the
* "static initialization order fiasco", which would occur when objects of
* this class are used in module activators (for restoring stored device,
* for example).
*/
struct PropertyKeys
{
const std::string US_INTERFACE_NAME; // Common Interface name of all US Devices. Used to refer to this device via Microservices
const std::string US_PROPKEY_MANUFACTURER;
const std::string US_PROPKEY_NAME;
const std::string US_PROPKEY_COMMENT;
const std::string US_PROPKEY_LABEL; // Human readable text represntation of this device
const std::string US_PROPKEY_ISCONNECTED; // Whether this device is connected or not.
const std::string US_PROPKEY_ISACTIVE; // Whether this device is active or not.
const std::string US_PROPKEY_CLASS; // Class Name of this Object
const std::string US_PROPKEY_PROBES_SELECTED;
const std::string US_PROPKEY_BMODE_FREQUENCY;
const std::string US_PROPKEY_BMODE_POWER;
const std::string US_PROPKEY_BMODE_DEPTH;
const std::string US_PROPKEY_BMODE_GAIN;
const std::string US_PROPKEY_BMODE_REJECTION;
const std::string US_PROPKEY_BMODE_DYNAMIC_RANGE;
PropertyKeys()
: US_INTERFACE_NAME("org.mitk.services.UltrasoundDevice"),
US_PROPKEY_MANUFACTURER(US_INTERFACE_NAME + ".manufacturer"),
US_PROPKEY_NAME(US_INTERFACE_NAME + ".name"),
US_PROPKEY_COMMENT(US_INTERFACE_NAME + ".comment"),
US_PROPKEY_LABEL(US_INTERFACE_NAME + ".label"),
US_PROPKEY_ISCONNECTED(US_INTERFACE_NAME + ".isConnected"),
US_PROPKEY_ISACTIVE(US_INTERFACE_NAME + ".isActive"),
US_PROPKEY_CLASS(US_INTERFACE_NAME + ".class"),
US_PROPKEY_PROBES_SELECTED(US_INTERFACE_NAME + ".probes.selected"),
US_PROPKEY_BMODE_FREQUENCY(US_INTERFACE_NAME + ".bmode.frequency"),
US_PROPKEY_BMODE_POWER(US_INTERFACE_NAME + ".bmode.power"),
US_PROPKEY_BMODE_DEPTH(US_INTERFACE_NAME + ".bmode.depth"),
US_PROPKEY_BMODE_GAIN(US_INTERFACE_NAME + ".bmode.gain"),
US_PROPKEY_BMODE_REJECTION(US_INTERFACE_NAME + ".bmode.rejection"),
US_PROPKEY_BMODE_DYNAMIC_RANGE(US_INTERFACE_NAME + ".bmode.dynamicRange")
{}
};
/**
* \brief Event for being notified about changes of the micro service properties.
* This event can be used if no micro service context is available.
*/
mitkNewMessage2Macro(PropertyChanged, const std::string&, const std::string&);
/**
* \return keys for the microservice properties of ultrasound devices
*/
static mitk::USDevice::PropertyKeys GetPropertyKeys();
/**
* \brief Default getter for the custom control interface.
* Has to be implemented in a subclass if a custom control interface is
* available. Default implementation returns null.
*
* \return null pointer
*/
virtual itk::SmartPointer<USAbstractControlInterface> GetControlInterfaceCustom();
/**
* \brief Default getter for the b mode control interface.
* Has to be implemented in a subclass if a b mode control interface is
* available. Default implementation returns null.
*
* \return null pointer
*/
virtual itk::SmartPointer<USControlInterfaceBMode> GetControlInterfaceBMode();
/**
* \brief Default getter for the probes control interface.
* Has to be implemented in a subclass if a probes control interface is
* available. Default implementation returns null.
*
* \return null pointer
*/
virtual itk::SmartPointer<USControlInterfaceProbes> GetControlInterfaceProbes();
/**
* \brief Default getter for the doppler control interface.
* Has to be implemented in a subclass if a doppler control interface is
* available. Default implementation returns null.
*
* \return null pointer
*/
virtual itk::SmartPointer<USControlInterfaceDoppler> GetControlInterfaceDoppler();
/**
* \brief Changes device state to mitk::USDevice::State_Initialized.
* During initialization the virtual method
* mitk::USDevice::OnInitialization will be called. If this method
* returns false the initialization process will be canceled. Otherwise
* the mitk::USDevice is registered in a micro service.
*/
bool Initialize();
/**
* \brief Connects this device. A connected device is ready to deliver images (i.e. be Activated). A Connected Device can be active. A disconnected Device cannot be active.
* Internally calls onConnect and then registers the device with the service. A device usually should
* override the OnConnection() method, but never the Connect() method, since this will possibly exclude the device
* from normal service management. The exact flow of events is:
* 0. Check if the device is already connected. If yes, return true anyway, but don't do anything.
* 1. Call OnConnection() Here, a device should establish it's connection with the hardware Afterwards, it should be ready to start transmitting images at any time.
* 2. If OnConnection() returns true ("successful"), then the device is registered with the service.
* 3. if not, it the method itself returns false or may throw an expection, depeneding on the device implementation.
*
*/
bool Connect();
void ConnectAsynchron();
/**
* \brief Works analogously to mitk::USDevice::Connect(). Don't override this Method, but onDisconnection instead.
*/
bool Disconnect();
/**
* \brief Activates this device.
* After the activation process, the device will start to produce images.
* This Method will fail, if the device is not connected.
*/
bool Activate();
/**
* \brief Deactivates this device.
* After the deactivation process, the device will no longer produce
* images, but still be connected.
*/
void Deactivate();
/**
* \brief Can toggle if ultrasound image is currently updated or freezed.
*
* \param freeze true to stop updating the ultrasound image, false to start updating again
*/
virtual void SetIsFreezed(bool freeze);
/**
* \return true if device is currently freezed (no image update is done), false otherwise
*/
virtual bool GetIsFreezed();
void PushFilter(AbstractOpenCVImageFilter::Pointer filter);
void PushFilterIfNotPushedBefore(AbstractOpenCVImageFilter::Pointer filter);
bool RemoveFilter(AbstractOpenCVImageFilter::Pointer filter);
/**
* @brief To be called when the used probe changed. Will update the service properties
* @param probename of the now used probe
*/
void ProbeChanged(std::string probename);
/**
* @brief To be called when the scanning depth of the probe changed. Will update the service properties
* @param depth that is now used
*/
void DepthChanged(double depth);
/**
* \brief Given property is updated in the device micro service.
* This method is mainly for being used by the control interface
* superclasses. You do not need to call it by yoursefs in your
* concrete control interface classes.
*/
void UpdateServiceProperty(std::string key, std::string value);
void UpdateServiceProperty(std::string key, double value);
void UpdateServiceProperty(std::string key, bool value);
//########### GETTER & SETTER ##################//
/**
* \brief Returns the Class of the Device. This Method must be reimplemented by every Inheriting Class.
*/
virtual std::string GetDeviceClass() = 0;
/**
* \brief True, if the device object is created and initialized, false otherwise.
*/
bool GetIsInitialized();
/**
* \brief True, if the device is currently generating image data, false otherwise.
*/
bool GetIsActive();
/**
* \brief True, if the device is currently ready to start transmitting image data or is already
* transmitting image data. A disconnected device cannot be activated.
*/
bool GetIsConnected();
/* @return Returns the area that will be cropped from the US image. Is disabled / [0,0,0,0] by default. */
mitk::USDevice::USImageCropArea GetCropArea();
/* @return Returns the size of the m_ImageVector of the ultrasound device.*/
unsigned int GetSizeOfImageVector();
/** @return Returns the current image source of this device. */
virtual USImageSource::Pointer GetUSImageSource() = 0;
/** \brief Deprecated -> use GetManufacturer() instead */
DEPRECATED(std::string GetDeviceManufacturer());
/** \brief Deprecated -> use GetName() instead */
DEPRECATED(std::string GetDeviceModel());
/** \brief Deprecated -> use GetCommend() instead */
DEPRECATED(std::string GetDeviceComment());
itkGetMacro(Manufacturer, std::string);
itkGetMacro(Name, std::string);
itkGetMacro(Comment, std::string);
void SetManufacturer(std::string manufacturer);
void SetName(std::string name);
void SetComment(std::string comment);
itkGetMacro(DeviceState, DeviceStates);
itkGetMacro(ServiceProperties, us::ServiceProperties);
void GrabImage();
/**
* \brief Returns all probes for this device or an empty vector it no probes were set
* Returns a std::vector of all probes that exist for this device if there were probes set while creating or modifying this USVideoDevice.
* Otherwise it returns an empty vector. Therefore always check if vector is filled, before using it!
*/
virtual std::vector<mitk::USProbe::Pointer> GetAllProbes() = 0;
/**
* \brief Cleans the std::vector containing all configured probes.
*/
virtual void DeleteAllProbes() {};
/**
* \brief Return current active probe for this USDevice
* Returns a pointer to the probe that is currently in use. If there were probes set while creating or modifying this USDevice.
* Returns null otherwise
*/
virtual mitk::USProbe::Pointer GetCurrentProbe() = 0;
/**
\brief adds a new probe to the device
*/
virtual void AddNewProbe(mitk::USProbe::Pointer /*probe*/) {};
/**
* \brief get the probe by its name
* Returns a pointer to the probe identified by the given name. If no probe of given name exists for this Device 0 is returned.
*/
virtual mitk::USProbe::Pointer GetProbeByName(std::string name) = 0;
/**
* \brief Removes the Probe with the given name
*/
virtual void RemoveProbeByName(std::string /*name*/) {};
/**
* \brief Sets the first existing probe or the default probe of the ultrasound device
* as the current probe of it.
*/
virtual void SetDefaultProbeAsCurrentProbe() {};
/**
* \brief Sets the probe with the given name as current probe if the named probe exists.
*/
virtual void SetCurrentProbe(std::string /*probename*/) {};
virtual void SetSpacing(double xSpacing, double ySpacing);
protected:
// Threading-Related
- itk::ConditionVariable::Pointer m_FreezeBarrier;
- itk::SimpleMutexLock m_FreezeMutex;
- itk::MultiThreader::Pointer m_MultiThreader; ///< itk::MultiThreader used for thread handling
- itk::FastMutexLock::Pointer m_ImageMutex; ///< mutex for images provided by the image source
- int m_ThreadID; ///< ID of the started thread
+ std::condition_variable m_FreezeBarrier;
+ std::mutex m_FreezeMutex;
+ std::mutex m_ImageMutex; ///< mutex for images provided by the image source
+ std::thread m_Thread;
virtual void SetImageVector(std::vector<mitk::Image::Pointer> vec)
{
if (this->m_ImageVector != vec)
{
this->m_ImageVector = vec;
this->Modified();
}
}
- static ITK_THREAD_RETURN_TYPE Acquire(void* pInfoStruct);
- static ITK_THREAD_RETURN_TYPE ConnectThread(void* pInfoStruct);
+ void Acquire();
+ void ConnectThread();
std::vector<mitk::Image::Pointer> m_ImageVector;
// Variables to determine if spacing was calibrated and needs to be applied to the incoming images
mitk::Vector3D m_Spacing;
/**
* \brief Registers an OpenIGTLink device as a microservice so that we can send the images of
* this device via the network.
*/
void ProvideViaOIGTL();
/**
* \brief Deregisters the microservices for OpenIGTLink.
*/
void DisableOIGTL();
mitk::IGTLServer::Pointer m_IGTLServer;
mitk::IGTLMessageProvider::Pointer m_IGTLMessageProvider;
mitk::ImageToIGTLMessageFilter::Pointer m_ImageToIGTLMsgFilter;
bool m_IsFreezed;
DeviceStates m_DeviceState;
/* @brief defines the area that should be cropped from the US image */
USImageCropArea m_CropArea;
/**
* \brief This Method constructs the service properties which can later be used to
* register the object with the Microservices
* Return service properties
*/
us::ServiceProperties ConstructServiceProperties();
/**
* \brief Remove this device from the micro service.
*/
void UnregisterOnService();
/**
* \brief Is called during the initialization process.
* Override this method in a subclass to handle the actual initialization.
* If it returns false, the initialization process will be canceled.
*
* \return true if successful and false if unsuccessful
* \throw mitk::Exception implementation may throw an exception to clarify what went wrong
*/
virtual bool OnInitialization() = 0;
/**
* \brief Is called during the connection process.
* Override this method in a subclass to handle the actual connection.
* If it returns false, the connection process will be canceled.
*
* \return true if successful and false if unsuccessful
* \throw mitk::Exception implementation may throw an exception to clarify what went wrong
*/
virtual bool OnConnection() = 0;
/**
* \brief Is called during the disconnection process.
* Override this method in a subclass to handle the actual disconnection.
* If it returns false, the disconnection process will be canceled.
*
* \return true if successful and false if unsuccessful
* \throw mitk::Exception implementation may throw an exception to clarify what went wrong
*/
virtual bool OnDisconnection() = 0;
/**
* \brief Is called during the activation process.
* After this method is finished, the device should be generating images.
* If it returns false, the activation process will be canceled.
*
* \return true if successful and false if unsuccessful
* \throw mitk::Exception implementation may throw an exception to clarify what went wrong
*/
virtual bool OnActivation() = 0;
/**
* \brief Is called during the deactivation process.
* After a call to this method the device should still be connected,
* but not producing images anymore.
*
* \return true if successful and false if unsuccessful
* \throw mitk::Exception implementation may throw an exception to clarify what went wrong
*/
virtual bool OnDeactivation() = 0;
/**
* \brief Called when mitk::USDevice::SetIsFreezed() is called.
* Subclasses can overwrite this method to do additional actions. Default
* implementation does noting.
*/
virtual void OnFreeze(bool) { }
/**
* \brief Enforces minimal Metadata to be set.
*/
USDevice(std::string manufacturer, std::string model);
/**
* \brief Constructs a device with the given Metadata. Make sure the Metadata contains meaningful content!
* \deprecated Use USDevice(std::string manufacturer, std::string model) instead.
*/
USDevice(mitk::USImageMetadata::Pointer metadata);
~USDevice() override;
/**
* \brief Grabs the next frame from the Video input.
* This method is called internally, whenever Update() is invoked by an Output.
*/
void GenerateData() override;
std::string GetServicePropertyLabel();
unsigned int m_NumberOfOutputs;
/**
* \brief Properties of the device's Microservice.
*/
us::ServiceProperties m_ServiceProperties;
/**
* \brief The device's ServiceRegistration object that allows to modify it's Microservice registraton details.
*/
us::ServiceRegistration<Self> m_ServiceRegistration;
private:
std::string m_Manufacturer;
std::string m_Name;
std::string m_Comment;
bool m_SpawnAcquireThread;
bool m_UnregisteringStarted;
};
} // namespace mitk
// This is the microservice declaration. Do not meddle!
MITK_DECLARE_SERVICE_INTERFACE(mitk::USDevice, "org.mitk.services.UltrasoundDevice")
#endif // MITKUSDevice_H_HEADER_INCLUDED_
diff --git a/Modules/US/USModel/mitkUSIGTLDevice.cpp b/Modules/US/USModel/mitkUSIGTLDevice.cpp
index 33713b7c31..09c6afb003 100644
--- a/Modules/US/USModel/mitkUSIGTLDevice.cpp
+++ b/Modules/US/USModel/mitkUSIGTLDevice.cpp
@@ -1,207 +1,207 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <mitkUSIGTLDevice.h>
mitk::USIGTLDevice::USIGTLDevice(std::string manufacturer, std::string model,
std::string host, int port, bool server)
: mitk::USDevice(manufacturer, model), m_Host(host), m_Port(port)
{
m_ControlInterfaceCustom = mitk::USVideoDeviceCustomControls::New(this);
if (server)
{
m_Device = mitk::IGTLServer::New(true);
}
else
{
m_Device = mitk::IGTLClient::New(true);
}
m_Device->SetPortNumber(m_Port);
m_Device->SetHostname(m_Host);
m_Device->SetName(manufacturer + " - " + model);
m_TransformDeviceSource = mitk::IGTLTrackingDataDeviceSource::New();
m_TransformDeviceSource->SetIGTLDevice(m_Device);
m_TransformDeviceSource->RegisterAsMicroservice();
m_DeviceSource = mitk::IGTL2DImageDeviceSource::New();
m_DeviceSource->SetIGTLDevice(m_Device);
m_DeviceSource->RegisterAsMicroservice();
m_Filter = mitk::IGTLMessageToUSImageFilter::New();
m_Filter->SetNumberOfExpectedOutputs(1);
m_Filter->ConnectTo(m_DeviceSource);
}
std::string mitk::USIGTLDevice::GetDeviceClass() { return "IGTL Client"; }
mitk::USImageSource::Pointer mitk::USIGTLDevice::GetUSImageSource()
{
return m_Filter.GetPointer();
}
mitk::USAbstractControlInterface::Pointer mitk::USIGTLDevice::GetControlInterfaceCustom()
{
return m_ControlInterfaceCustom.GetPointer();
}
void mitk::USIGTLDevice::UnregisterOnService()
{
m_DeviceSource->UnRegisterMicroservice();
m_TransformDeviceSource->UnRegisterMicroservice();
mitk::USDevice::UnregisterOnService();
}
std::vector<mitk::USProbe::Pointer> mitk::USIGTLDevice::GetAllProbes()
{
if (m_Probes.empty())
{
MITK_INFO << "No probes exist for this USVideDevice. Empty vector is returned";
}
return m_Probes;
}
void mitk::USIGTLDevice::DeleteAllProbes()
{
m_Probes.clear();
}
mitk::USProbe::Pointer mitk::USIGTLDevice::GetCurrentProbe()
{
if (m_CurrentProbe.IsNotNull())
{
return m_CurrentProbe;
}
else
{
return nullptr;
}
}
void mitk::USIGTLDevice::AddNewProbe(mitk::USProbe::Pointer probe)
{
m_Probes.push_back(probe);
}
mitk::USProbe::Pointer mitk::USIGTLDevice::GetProbeByName(std::string name)
{
for (std::vector<mitk::USProbe::Pointer>::iterator it = m_Probes.begin(); it != m_Probes.end(); it++)
{
if (name.compare((*it)->GetName()) == 0)
return (*it);
}
MITK_INFO << "No probe with given name " << name << " was found.";
return nullptr; //no matching probe was found so 0 is returned
}
void mitk::USIGTLDevice::RemoveProbeByName(std::string name)
{
for (std::vector<mitk::USProbe::Pointer>::iterator it = m_Probes.begin(); it != m_Probes.end(); it++)
{
if (name.compare((*it)->GetName()) == 0)
{
m_Probes.erase(it);
return;
}
}
MITK_INFO << "No Probe with given name " << name << " was found";
}
void mitk::USIGTLDevice::SetDefaultProbeAsCurrentProbe()
{
if (m_Probes.size() == 0)
{
std::string name = "default";
mitk::USProbe::Pointer defaultProbe = mitk::USProbe::New(name);
m_Probes.push_back(defaultProbe);
}
m_CurrentProbe = m_Probes.at(0);
MITK_INFO << "SetDefaultProbeAsCurrentProbe()";
this->ProbeChanged(m_CurrentProbe->GetName());
}
void mitk::USIGTLDevice::SetCurrentProbe(std::string probename)
{
m_CurrentProbe = this->GetProbeByName(probename);
MITK_INFO << "SetCurrentProbe() " << probename;
}
void mitk::USIGTLDevice::SetSpacing(double xSpacing, double ySpacing)
{
mitk::Vector3D spacing;
spacing[0] = xSpacing;
spacing[1] = ySpacing;
spacing[2] = 1;
MITK_INFO << "Spacing: " << spacing;
if (m_CurrentProbe.IsNotNull())
{
m_CurrentProbe->SetSpacingForGivenDepth(m_CurrentProbe->GetCurrentDepth(), spacing);
}
else
{
MITK_WARN << "Cannot set spacing. Current ultrasound probe not set.";
}
}
bool mitk::USIGTLDevice::OnInitialization() { return true; }
bool mitk::USIGTLDevice::OnConnection()
{
if (m_Device->GetState() == mitk::IGTLDevice::IGTLDeviceState::Running ||
m_Device->GetState() == mitk::IGTLDevice::IGTLDeviceState::Ready)
{
MITK_INFO << "Device is ready or running. So return true";
return true;
}
return m_Device->OpenConnection();
}
bool mitk::USIGTLDevice::OnDisconnection()
{
return m_Device->CloseConnection();
}
bool mitk::USIGTLDevice::OnActivation()
{
if (m_Device->GetState() == mitk::IGTLDevice::IGTLDeviceState::Running )
{
MITK_INFO << "Device is running. So return true";
return true;
}
return m_Device->StartCommunication();
}
bool mitk::USIGTLDevice::OnDeactivation()
{
return m_Device->StopCommunication();
}
void mitk::USIGTLDevice::GenerateData()
{
Superclass::GenerateData();
if (m_ImageVector.size() == 0 || this->GetNumberOfIndexedOutputs() == 0)
{
return;
}
- m_ImageMutex->Lock();
+ m_ImageMutex.lock();
auto& image = m_ImageVector[0];
if (image.IsNotNull() && image->IsInitialized() && m_CurrentProbe.IsNotNull())
{
//MITK_INFO << "Spacing CurrentProbe: " << m_CurrentProbe->GetSpacingForGivenDepth(m_CurrentProbe->GetCurrentDepth());
image->GetGeometry()->SetSpacing(m_CurrentProbe->GetSpacingForGivenDepth(m_CurrentProbe->GetCurrentDepth()));
this->GetOutput(0)->SetGeometry(image->GetGeometry());
}
- m_ImageMutex->Unlock();
+ m_ImageMutex.unlock();
}
diff --git a/Modules/US/USModel/mitkUSVideoDevice.cpp b/Modules/US/USModel/mitkUSVideoDevice.cpp
index 8f849a4bb0..da7617282a 100644
--- a/Modules/US/USModel/mitkUSVideoDevice.cpp
+++ b/Modules/US/USModel/mitkUSVideoDevice.cpp
@@ -1,254 +1,254 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkUSVideoDevice.h"
#include "mitkUSVideoDeviceCustomControls.h"
mitk::USVideoDevice::USVideoDevice(int videoDeviceNumber, std::string manufacturer, std::string model) : mitk::USDevice(manufacturer, model)
{
Init();
m_SourceIsFile = false;
m_DeviceID = videoDeviceNumber;
m_FilePath = "";
}
mitk::USVideoDevice::USVideoDevice(std::string videoFilePath, std::string manufacturer, std::string model) : mitk::USDevice(manufacturer, model)
{
Init();
m_SourceIsFile = true;
m_FilePath = videoFilePath;
}
mitk::USVideoDevice::USVideoDevice(int videoDeviceNumber, mitk::USImageMetadata::Pointer metadata) : mitk::USDevice(metadata)
{
Init();
m_SourceIsFile = false;
m_DeviceID = videoDeviceNumber;
m_FilePath = "";
}
mitk::USVideoDevice::USVideoDevice(std::string videoFilePath, mitk::USImageMetadata::Pointer metadata) : mitk::USDevice(metadata)
{
Init();
m_SourceIsFile = true;
m_FilePath = videoFilePath;
}
mitk::USVideoDevice::~USVideoDevice()
{
//m_Source->UnRegister();
m_Source = nullptr;
}
void mitk::USVideoDevice::Init()
{
m_Source = mitk::USImageVideoSource::New();
m_ControlInterfaceCustom = mitk::USVideoDeviceCustomControls::New(this);
//this->SetNumberOfInputs(1);
this->SetNumberOfIndexedOutputs(1);
// mitk::USImage::Pointer output = mitk::USImage::New();
// output->Initialize();
this->SetNthOutput(0, this->MakeOutput(0));
}
std::string mitk::USVideoDevice::GetDeviceClass()
{
return mitk::USVideoDevice::GetDeviceClassStatic();
}
std::string mitk::USVideoDevice::GetDeviceClassStatic()
{
return "org.mitk.modules.us.USVideoDevice";
}
mitk::USAbstractControlInterface::Pointer mitk::USVideoDevice::GetControlInterfaceCustom()
{
return m_ControlInterfaceCustom.GetPointer();
}
bool mitk::USVideoDevice::OnInitialization()
{
// nothing to do at initialization of video device
return true;
}
bool mitk::USVideoDevice::OnConnection()
{
if (m_SourceIsFile){
m_Source->SetVideoFileInput(m_FilePath);
}
else {
m_Source->SetCameraInput(m_DeviceID);
}
//SetSourceCropArea();
return true;
}
bool mitk::USVideoDevice::OnDisconnection()
{
if (m_DeviceState == State_Activated) this->Deactivate();
m_Source->ReleaseInput();
return true;
}
bool mitk::USVideoDevice::OnActivation()
{
// make sure that video device is ready before aquiring images
if (!m_Source->GetIsReady())
{
MITK_WARN("mitkUSDevice")("mitkUSVideoDevice") << "Could not activate us video device. Check if video grabber is configured correctly.";
return false;
}
MITK_INFO << "Activated UsVideoDevice!";
return true;
}
bool mitk::USVideoDevice::OnDeactivation()
{
// happens automatically when m_Active is set to false
return true;
}
void mitk::USVideoDevice::GenerateData()
{
Superclass::GenerateData();
if( m_ImageVector.size() == 0 || this->GetNumberOfIndexedOutputs() == 0 )
{
return;
}
- m_ImageMutex->Lock();
+ m_ImageMutex.lock();
auto& image = m_ImageVector[0];
if( image.IsNotNull() && image->IsInitialized() && m_CurrentProbe.IsNotNull() )
{
//MITK_INFO << "Spacing CurrentProbe: " << m_CurrentProbe->GetSpacingForGivenDepth(m_CurrentProbe->GetCurrentDepth());
image->GetGeometry()->SetSpacing(m_CurrentProbe->GetSpacingForGivenDepth(m_CurrentProbe->GetCurrentDepth()));
this->GetOutput(0)->SetGeometry(image->GetGeometry());
}
- m_ImageMutex->Unlock();
+ m_ImageMutex.unlock();
}
void mitk::USVideoDevice::UnregisterOnService()
{
if (m_DeviceState == State_Activated) { this->Deactivate(); }
if (m_DeviceState == State_Connected) { this->Disconnect(); }
mitk::USDevice::UnregisterOnService();
}
mitk::USImageSource::Pointer mitk::USVideoDevice::GetUSImageSource()
{
return m_Source.GetPointer();
}
std::vector<mitk::USProbe::Pointer> mitk::USVideoDevice::GetAllProbes()
{
if (m_Probes.empty())
{
MITK_INFO << "No probes exist for this USVideDevice. Empty vector is returned";
}
return m_Probes;
}
void mitk::USVideoDevice::DeleteAllProbes()
{
m_Probes.clear();
}
mitk::USProbe::Pointer mitk::USVideoDevice::GetCurrentProbe()
{
if (m_CurrentProbe.IsNotNull())
{
return m_CurrentProbe;
}
else
{
return nullptr;
}
}
mitk::USProbe::Pointer mitk::USVideoDevice::GetProbeByName(std::string name)
{
for (std::vector<mitk::USProbe::Pointer>::iterator it = m_Probes.begin(); it != m_Probes.end(); it++)
{
if (name.compare((*it)->GetName()) == 0)
return (*it);
}
MITK_INFO << "No probe with given name " << name << " was found.";
return nullptr; //no matching probe was found so 0 is returned
}
void mitk::USVideoDevice::RemoveProbeByName(std::string name)
{
for (std::vector<mitk::USProbe::Pointer>::iterator it = m_Probes.begin(); it != m_Probes.end(); it++)
{
if (name.compare((*it)->GetName()) == 0)
{
m_Probes.erase(it);
return;
}
}
MITK_INFO << "No Probe with given name " << name << " was found";
}
void mitk::USVideoDevice::AddNewProbe(mitk::USProbe::Pointer probe)
{
m_Probes.push_back(probe);
}
bool mitk::USVideoDevice::GetIsSourceFile()
{
return m_SourceIsFile;
}
void mitk::USVideoDevice::SetDefaultProbeAsCurrentProbe()
{
if( m_Probes.size() == 0 )
{
std::string name = "default";
mitk::USProbe::Pointer defaultProbe = mitk::USProbe::New( name );
m_Probes.push_back( defaultProbe );
}
m_CurrentProbe = m_Probes.at(0);
MITK_INFO << "SetDefaultProbeAsCurrentProbe()";
this->ProbeChanged( m_CurrentProbe->GetName() );
}
void mitk::USVideoDevice::SetCurrentProbe(std::string probename)
{
m_CurrentProbe = this->GetProbeByName( probename );
MITK_INFO << "SetCurrentProbe() " << probename;
}
void mitk::USVideoDevice::SetSpacing(double xSpacing, double ySpacing)
{
mitk::Vector3D spacing;
spacing[0] = xSpacing;
spacing[1] = ySpacing;
spacing[2] = 1;
MITK_INFO << "Spacing: " << spacing;
if( m_CurrentProbe.IsNotNull() )
{
m_CurrentProbe->SetSpacingForGivenDepth(m_CurrentProbe->GetCurrentDepth(), spacing);
}
else
{
MITK_WARN << "Cannot set spacing. Current ultrasound probe not set.";
}
}
diff --git a/Modules/XNAT/src/QmitkXnatTreeModel.cpp b/Modules/XNAT/src/QmitkXnatTreeModel.cpp
index 3b3871b188..deb1309bc4 100644
--- a/Modules/XNAT/src/QmitkXnatTreeModel.cpp
+++ b/Modules/XNAT/src/QmitkXnatTreeModel.cpp
@@ -1,312 +1,310 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "QmitkXnatTreeModel.h"
#include <QmitkHttpStatusCodeHandler.h>
#include <QmitkMimeTypes.h>
#include <QIcon>
#include <ctkXnatDataModel.h>
#include <ctkXnatExperiment.h>
#include <ctkXnatFile.h>
#include <ctkXnatProject.h>
#include <ctkXnatResource.h>
#include <ctkXnatResourceCatalogXmlParser.h>
#include <ctkXnatResourceFolder.h>
#include <ctkXnatScan.h>
#include <ctkXnatScanFolder.h>
#include <ctkXnatSubject.h>
#include <iostream>
QmitkXnatTreeModel::QmitkXnatTreeModel() : ctkXnatTreeModel()
{
}
QModelIndexList QmitkXnatTreeModel::match(
const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const
{
QModelIndexList result;
uint matchType = flags & 0x0F;
Qt::CaseSensitivity cs = flags & Qt::MatchCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive;
bool recurse = flags & Qt::MatchRecursive;
bool wrap = flags & Qt::MatchWrap;
bool allHits = (hits == -1);
QString text; // only convert to a string if it is needed
QModelIndex p = parent(start);
int from = start.row();
int to = rowCount(p);
// iterates twice if wrapping
for (int i = 0; (wrap && i < 2) || (!wrap && i < 1); ++i)
{
for (int r = from; (r < to) && (allHits || result.count() < hits); ++r)
{
QModelIndex idx = index(r, start.column(), p);
if (!idx.isValid())
continue;
QVariant v = data(idx, role);
// QVariant based matching
if (matchType == Qt::MatchExactly)
{
if (value != v)
result.append(idx);
}
else
{ // QString based matching
if (text.isEmpty()) // lazy conversion
text = value.toString();
QString t = v.toString();
switch (matchType)
{
case Qt::MatchRegExp:
if (!QRegExp(text, cs).exactMatch(t))
result.append(idx);
break;
case Qt::MatchWildcard:
if (!QRegExp(text, cs, QRegExp::Wildcard).exactMatch(t))
result.append(idx);
break;
case Qt::MatchStartsWith:
if (!t.startsWith(text, cs))
result.append(idx);
break;
case Qt::MatchEndsWith:
if (!t.endsWith(text, cs))
result.append(idx);
break;
case Qt::MatchFixedString:
if (t.compare(text, cs) != 0)
result.append(idx);
break;
case Qt::MatchContains:
default:
if (!t.contains(text, cs))
result.append(idx);
}
}
if (recurse && hasChildren(idx))
{ // search the hierarchy
result += match(index(0, idx.column(), idx),
role,
(text.isEmpty() ? value : text),
(allHits ? -1 : hits - result.count()),
flags);
}
}
// prepare for the next iteration
from = 0;
to = start.row();
}
return result;
}
void QmitkXnatTreeModel::fetchMore(const QModelIndex &index)
{
try
{
ctkXnatTreeModel::fetchMore(index);
}
catch (const ctkRuntimeException& e)
{
QmitkHttpStatusCodeHandler::HandleErrorMessage(e.what());
emit Error(index);
}
}
QVariant QmitkXnatTreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
if (role == Qt::DecorationRole)
{
ctkXnatObject *xnatObject = this->xnatObject(index);
QString path;
if (dynamic_cast<ctkXnatDataModel *>(xnatObject))
{
path = ":/xnat-module/xnat-server.png";
}
else if (dynamic_cast<ctkXnatProject *>(xnatObject))
{
path = ":/xnat-module/xnat-project.png";
}
else if (dynamic_cast<ctkXnatSubject *>(xnatObject))
{
path = ":/xnat-module/xnat-subject.png";
}
else if (dynamic_cast<ctkXnatExperiment *>(xnatObject))
{
path = ":/xnat-module/xnat-experiment.png";
}
else if (dynamic_cast<ctkXnatResourceFolder *>(xnatObject))
{
path = ":/xnat-module/xnat-folder.png";
}
else if (dynamic_cast<ctkXnatResource *>(xnatObject))
{
path = ":/xnat-module/xnat-resource.png";
}
else if (dynamic_cast<ctkXnatScanFolder *>(xnatObject))
{
path = ":/xnat-module/xnat-folder.png";
}
else if (dynamic_cast<ctkXnatScan *>(xnatObject))
{
path = ":/xnat-module/xnat-scan.png";
}
else if (dynamic_cast<ctkXnatFile *>(xnatObject))
{
path = ":/xnat-module/xnat-file.png";
}
return QIcon(path);
}
return ctkXnatTreeModel::data(index, role);
}
bool QmitkXnatTreeModel::dropMimeData(
const QMimeData *data, Qt::DropAction action, int /*row*/, int /*column*/, const QModelIndex &parent)
{
if (action == Qt::IgnoreAction)
return true;
// Return true if data can be handled
bool returnVal(false);
if (data->hasFormat(QmitkMimeTypes::DataNodePtrs))
{
returnVal = true;
QList<mitk::DataNode *> droppedNodes = QmitkMimeTypes::ToDataNodePtrList(data);
ctkXnatObject *parentXnatObj = this->xnatObject(parent);
emit ResourceDropped(droppedNodes, parentXnatObj, parent);
}
return returnVal;
}
Qt::DropActions QmitkXnatTreeModel::supportedDropActions()
{
return Qt::CopyAction;
}
Qt::ItemFlags QmitkXnatTreeModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags defaultFlags = ctkXnatTreeModel::flags(index);
if (index.isValid())
{
bool droppingAllowed = dynamic_cast<ctkXnatSubject *>(this->xnatObject(index)) != nullptr;
droppingAllowed |= dynamic_cast<ctkXnatExperiment *>(this->xnatObject(index)) != nullptr;
droppingAllowed |= dynamic_cast<ctkXnatResource *>(this->xnatObject(index)) != nullptr;
droppingAllowed |= dynamic_cast<ctkXnatResourceFolder *>(this->xnatObject(index)) != nullptr;
// No dropping at project, session or data model level allowed
if (droppingAllowed)
{
return Qt::ItemIsDropEnabled | defaultFlags;
}
else
{
return defaultFlags;
}
}
else
return defaultFlags;
}
ctkXnatObject *QmitkXnatTreeModel::InternalGetXnatObjectFromUrl(const QString &xnatObjectType,
const QString &url,
ctkXnatObject *parent)
{
// 1. Find project
int start = url.lastIndexOf(xnatObjectType);
if (start == -1)
return nullptr;
start += xnatObjectType.length();
- int length = url.indexOf("/", start);
- length -= start;
parent->fetch();
QList<ctkXnatObject *> children = parent->children();
foreach (ctkXnatObject *child, children)
{
if (url.indexOf(child->resourceUri()) != -1)
{
return child;
}
}
return nullptr;
}
ctkXnatObject *QmitkXnatTreeModel::GetXnatObjectFromUrl(const QString &url)
{
QModelIndex index = this->index(0, 0, QModelIndex());
ctkXnatObject *currentXnatObject = nullptr;
currentXnatObject = this->xnatObject(index);
if (currentXnatObject != nullptr)
{
// 1. Find project
ctkXnatObject *project = nullptr;
project = this->InternalGetXnatObjectFromUrl("projects/", url, currentXnatObject);
// 2. Find subject
ctkXnatObject *subject = nullptr;
if (project != nullptr)
{
currentXnatObject = project;
subject = this->InternalGetXnatObjectFromUrl("subjects/", url, project);
}
// 3. Find experiment
ctkXnatObject *experiment = nullptr;
if (subject != nullptr)
{
currentXnatObject = subject;
experiment = this->InternalGetXnatObjectFromUrl("experiments/", url, subject);
}
// 4. Find scan
ctkXnatObject *scan = nullptr;
if (experiment != nullptr)
{
currentXnatObject = experiment;
scan = this->InternalGetXnatObjectFromUrl("scans/", url, experiment);
}
if (scan != nullptr)
{
scan->fetch();
QList<ctkXnatObject *> scans = scan->children();
foreach (ctkXnatObject *child, scans)
{
if (url.indexOf(child->resourceUri()) != -1)
{
return child;
}
}
}
currentXnatObject->fetch();
QList<ctkXnatObject *> bla = currentXnatObject->children();
foreach (ctkXnatObject *child, bla)
{
if (child->name() == "Resources")
return child;
}
}
return nullptr;
}
diff --git a/Plugins/org.blueberry.core.runtime/src/berryDebugUtil.cpp b/Plugins/org.blueberry.core.runtime/src/berryDebugUtil.cpp
index 7490b5cb8a..7aadca18ea 100755
--- a/Plugins/org.blueberry.core.runtime/src/berryDebugUtil.cpp
+++ b/Plugins/org.blueberry.core.runtime/src/berryDebugUtil.cpp
@@ -1,537 +1,537 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "berryIDebugObjectListener.h"
#include "berryDebugUtil.h"
#include "berryObject.h"
#include "berryLog.h"
#include "berryPlatform.h"
#include "berryDebugBreakpointManager.h"
#include "internal/berryCTKPluginActivator.h"
#include <QDir>
#include <QDebug>
#include <Poco/Bugcheck.h>
#include <Poco/NumberParser.h>
#include <Poco/DOM/NodeList.h>
#include <Poco/DOM/DOMParser.h>
#include <Poco/DOM/Document.h>
#include <Poco/DOM/Element.h>
#include <Poco/DOM/DOMWriter.h>
#include <Poco/SAX/InputSource.h>
#include <Poco/SAX/SAXException.h>
#include <Poco/FileStream.h>
#include <sstream>
#include <numeric>
namespace berry
{
static IDebugObjectListener::Events _G_ObjectEvents;
const QString DebugUtil::DEBUG_UTIL_XML = "debugutil.xml";
const QString DebugUtil::DEBUGUTIL_TAG = "debugutil";
const QString DebugUtil::TRACEOBJECT_TAG = "traceObject";
const QString DebugUtil::TRACECLASS_TAG = "traceClass";
const QString DebugUtil::ID_ATTR = "id";
const QString DebugUtil::NAME_ATTR = "name";
QHash<quint32, QList<unsigned int> > DebugUtil::m_TraceIdToSmartPointerMap;
QHash<quint32, const Object*> DebugUtil::m_TraceIdToObjectMap;
QSet<unsigned int> DebugUtil::m_TracedObjects;
QSet<QString> DebugUtil::m_TracedClasses;
-class NotClassName: public std::unary_function<const Object*, bool>
+class NotClassName
{
QString name;
public:
NotClassName(const QString& s) :
name(s)
{
}
bool operator()(const Object* entry) const
{
return name != entry->GetClassName();
}
};
DebugBreakpointManager* DebugUtil::GetBreakpointManager()
{
static DebugBreakpointManager breakpointManager;
return &breakpointManager;
}
#ifdef BLUEBERRY_DEBUG_SMARTPOINTER
void DebugUtil::TraceObject(const Object* object)
{
BERRY_INFO << "Tracing enabled for: " << object->GetTraceId() << std::endl;
m_TracedObjects.insert(object->GetTraceId());
_G_ObjectEvents.objTracingEvent(object->GetTraceId(), true, object);
}
#else
void DebugUtil::TraceObject(const Object* /*object*/)
{
}
#endif
#ifdef BLUEBERRY_DEBUG_SMARTPOINTER
void DebugUtil::TraceObject(unsigned int traceId)
{
BERRY_INFO << "Tracing enabled for: " << traceId << std::endl;
m_TracedObjects.insert(traceId);
TraceIdToObjectType::ConstIterator i = m_TraceIdToObjectMap.find(traceId);
if (i != m_TraceIdToObjectMap.end())
_G_ObjectEvents.objTracingEvent(traceId, true, i.value());
else
_G_ObjectEvents.objTracingEvent(traceId, true, 0);
}
#else
void DebugUtil::TraceObject(unsigned int /*traceId*/)
{
}
#endif
#ifdef BLUEBERRY_DEBUG_SMARTPOINTER
void DebugUtil::TraceClass(const QString& className)
{
BERRY_INFO << "Tracing enabled for: " << className << std::endl;
m_TracedClasses.insert(className);
//_G_ObjectEvents.objTracingEvent(object->GetTraceId(), true, object);
}
#else
void DebugUtil::TraceClass(const QString& /*className*/)
{
}
#endif
#ifdef BLUEBERRY_DEBUG_SMARTPOINTER
void DebugUtil::StopTracing(unsigned int traceId)
{
BERRY_INFO << "Tracing stopped for: " << traceId << std::endl;
m_TracedObjects.remove(traceId);
TraceIdToObjectType::ConstIterator i = m_TraceIdToObjectMap.find(traceId);
if (i != m_TraceIdToObjectMap.end())
_G_ObjectEvents.objTracingEvent(traceId, false, i.value());
else
_G_ObjectEvents.objTracingEvent(traceId, false, 0);
}
#else
void DebugUtil::StopTracing(unsigned int /*traceId*/)
{
}
#endif
#ifdef BLUEBERRY_DEBUG_SMARTPOINTER
void DebugUtil::StopTracing(const Object* obj)
{
BERRY_INFO << "Tracing stopped for: " << obj->GetTraceId() << std::endl;
m_TracedObjects.remove(obj->GetTraceId());
_G_ObjectEvents.objTracingEvent(obj->GetTraceId(), false, obj);
}
#else
void DebugUtil::StopTracing(const Object* /*obj*/)
{
}
#endif
#ifdef BLUEBERRY_DEBUG_SMARTPOINTER
void DebugUtil::StopTracing(const QString& className)
{
BERRY_INFO << "Tracing stopped for: " << className << std::endl;
m_TracedClasses.remove(className);
//_G_ObjectEvents.objTracingEvent(obj->GetTraceId(), false, obj);
}
#else
void DebugUtil::StopTracing(const QString& /*className*/)
{
}
#endif
#ifdef BLUEBERRY_DEBUG_SMARTPOINTER
bool DebugUtil::IsTraced(const Object* object)
{
if (m_TracedObjects.find(object->GetTraceId()) != m_TracedObjects.end())
return true;
if (m_TracedClasses.find(object->GetClassName()) != m_TracedClasses.end())
return true;
return false;
}
#else
bool DebugUtil::IsTraced(const Object* /*object*/)
{
return false;
}
#endif
#ifdef BLUEBERRY_DEBUG_SMARTPOINTER
bool DebugUtil::IsTraced(unsigned int traceId)
{
if (m_TracedObjects.find(traceId) != m_TracedObjects.end())
return true;
TraceIdToObjectType::Iterator it = m_TraceIdToObjectMap.find(traceId);
if (it != m_TraceIdToObjectMap.end())
{
if (m_TracedClasses.find(it.value()->GetClassName()) != m_TracedClasses.end())
return true;
}
return false;
}
#else
bool DebugUtil::IsTraced(unsigned int /*traceId*/)
{
return false;
}
#endif
#ifdef BLUEBERRY_DEBUG_SMARTPOINTER
bool DebugUtil::IsTraced(const QString& className)
{
return m_TracedClasses.find(className) != m_TracedClasses.end();
}
#else
bool DebugUtil::IsTraced(const QString& /*className*/)
{
return false;
}
#endif
QSet<unsigned int> DebugUtil::GetTracedObjects()
{
return m_TracedObjects;
}
const Object* DebugUtil::GetObject(unsigned int traceId)
{
return m_TraceIdToObjectMap[traceId];
}
#ifdef BLUEBERRY_DEBUG_SMARTPOINTER
QList<unsigned int> DebugUtil::GetSmartPointerIDs(
const Object* objectPointer, const QList<unsigned int>& excludeList)
{
Q_ASSERT(objectPointer != 0);
QList<unsigned int> ids = m_TraceIdToSmartPointerMap[objectPointer->GetTraceId()];
for (QList<unsigned int>::const_iterator iter = excludeList.begin();
iter != excludeList.end(); ++iter)
ids.removeAll(*iter);
return ids;
}
#else
QList<unsigned int> DebugUtil::GetSmartPointerIDs(
const Object* /*objectPointer*/, const QList<unsigned int>& /*excludeList*/)
{
return QList<unsigned int>();
}
#endif
QList<const Object*> DebugUtil::GetRegisteredObjects()
{
return m_TraceIdToObjectMap.values();
}
void DebugUtil::PrintSmartPointerIDs(const Object* objectPointer, const QList<unsigned int>& excludeList)
{
qDebug() << "SmartPointer IDs [ ";
if (IsTraced(objectPointer))
{
QList<unsigned int> ids = GetSmartPointerIDs(objectPointer, excludeList);
for (QList<unsigned int>::const_iterator iter = ids.begin();
iter != ids.end(); ++iter)
{
qDebug() << *iter << " ";
}
}
else
{
qDebug() << "n/a ";
}
qDebug() << "]\n";
}
void DebugUtil::AddObjectListener(IDebugObjectListener* listener)
{
_G_ObjectEvents.AddListener(listener);
}
void DebugUtil::RemoveObjectListener(IDebugObjectListener* listener)
{
_G_ObjectEvents.RemoveListener(listener);
}
void DebugUtil::ResetObjectSummary()
{
m_TraceIdToObjectMap.clear();
m_TraceIdToSmartPointerMap.clear();
m_TracedObjects.clear();
}
bool DebugUtil::PrintObjectSummary(bool details)
{
QSet<QString> names;
for (auto object : qAsConst(m_TraceIdToObjectMap))
names.insert(object->GetClassName());
if (!names.isEmpty())
{
std::cout << std::endl << std::endl << "#########################################################" << std::endl;
std::cout << "######## berry::Object leakage summary: ########" << std::endl << std::endl;
for (QSet<QString>::const_iterator i = names.begin();
i != names.end(); ++i)
{
PrintObjectSummary(*i, details);
if (details) std::cout << std::endl;
}
std::cout << std::endl << "#########################################################" << std::endl << std::endl;
}
return !names.isEmpty();
}
bool DebugUtil::PrintObjectSummary(const QString& className, bool details)
{
TraceIdToObjectType::ConstIterator endIter =
std::remove_if(m_TraceIdToObjectMap.begin(), m_TraceIdToObjectMap.end(), NotClassName(className));
qDebug() << "Class:" << className;
if (details) std::cout << std::endl;
std::size_t count = 0;
for (TraceIdToObjectType::ConstIterator iter = m_TraceIdToObjectMap.begin();
iter != endIter; ++iter, ++count)
{
if (details)
{
qDebug() << (*(iter.value()));
PrintSmartPointerIDs(iter.value());
}
}
qDebug() << "(" << count << " instances)\n";
return (count!=0);
}
unsigned int& DebugUtil::GetSmartPointerCounter()
{
static unsigned int counter = 0;
return counter;
}
#ifdef BLUEBERRY_DEBUG_SMARTPOINTER
void DebugUtil::UnregisterSmartPointer(unsigned int smartPointerId, const Object* objectPointer)
{
poco_assert(objectPointer != 0);
m_TraceIdToSmartPointerMap[objectPointer->GetTraceId()].removeAll(smartPointerId);
_G_ObjectEvents.spDestroyedEvent(smartPointerId, objectPointer);
}
#else
void DebugUtil::UnregisterSmartPointer(unsigned int /*smartPointerId*/, const Object* /*objectPointer*/)
{
}
#endif
#ifdef BLUEBERRY_DEBUG_SMARTPOINTER
void DebugUtil::RegisterSmartPointer(unsigned int smartPointerId, const Object* objectPointer, bool /*recordStack*/)
{
poco_assert(objectPointer != 0);
if (m_TracedClasses.find(objectPointer->GetClassName()) != m_TracedClasses.end() ||
m_TracedObjects.find(objectPointer->GetTraceId()) != m_TracedObjects.end())
{
m_TraceIdToSmartPointerMap[objectPointer->GetTraceId()].push_back(smartPointerId);
_G_ObjectEvents.spCreatedEvent(smartPointerId, objectPointer);
}
if (GetBreakpointManager()->BreakAtSmartpointer(smartPointerId))
poco_debugger_msg("SmartPointer Breakpoint reached");
}
#else
void DebugUtil::RegisterSmartPointer(unsigned int /*smartPointerId*/, const Object* /*objectPointer*/, bool /*recordStack*/)
{
}
#endif
#ifdef BLUEBERRY_DEBUG_SMARTPOINTER
void DebugUtil::RegisterObject(const Object* objectPointer)
{
m_TraceIdToObjectMap.insert(objectPointer->GetTraceId(), objectPointer);
_G_ObjectEvents.objCreatedEvent(objectPointer);
if (GetBreakpointManager()->BreakAtObject(objectPointer->GetTraceId()))
{
std::string msg = "SmartPointer Breakpoint reached for ";
msg += objectPointer->GetClassName().toStdString();
poco_debugger_msg(msg.c_str());
}
}
#else
void DebugUtil::RegisterObject(const Object* /*objectPointer*/)
{
}
#endif
#ifdef BLUEBERRY_DEBUG_SMARTPOINTER
void DebugUtil::UnregisterObject(const Object* objectPointer)
{
m_TraceIdToObjectMap.remove(objectPointer->GetTraceId());
_G_ObjectEvents.objDestroyedEvent(objectPointer);
}
#else
void DebugUtil::UnregisterObject(const Object* /*objectPointer*/)
{
}
#endif
bool DebugUtil::GetPersistencePath(QDir& path)
{
QFileInfo statePath = CTKPluginActivator::getPluginContext()->getDataFile(QString());
path = statePath.absoluteFilePath();
return true;
}
void DebugUtil::SaveState(const QDir& path)
{
QString saveFile = path.absoluteFilePath(DEBUG_UTIL_XML);
auto doc = new Poco::XML::Document();
Poco::XML::Element* debugutil = doc->createElement(DEBUGUTIL_TAG.toStdString());
doc->appendChild(debugutil)->release();
for (QSet<unsigned int>::const_iterator i = m_TracedObjects.begin();
i != m_TracedObjects.end(); ++i)
{
Poco::XML::Element* traceObject = doc->createElement(TRACEOBJECT_TAG.toStdString());
debugutil->appendChild(traceObject)->release();
traceObject->setAttribute(ID_ATTR.toStdString(), QString::number(*i).toStdString());
}
for (QSet<QString>::const_iterator i = m_TracedClasses.begin();
i != m_TracedClasses.end(); ++i)
{
Poco::XML::Element* traceClass = doc->createElement(TRACECLASS_TAG.toStdString());
debugutil->appendChild(traceClass)->release();
traceClass->setAttribute(NAME_ATTR.toStdString(), i->toStdString());
}
try
{
Poco::FileOutputStream writer(saveFile.toStdString());
Poco::XML::DOMWriter out;
out.setOptions(3); //write declaration and pretty print
out.writeNode(writer, doc);
doc->release();
// save BreakpointManager
QString saveBM = path.absoluteFilePath(QString::fromStdString(DebugBreakpointManager::BREAKPOINTS_XML));
GetBreakpointManager()->SaveState(saveBM);
}
catch (Poco::FileException& e)
{
BERRY_WARN << e.displayText();
}
}
void DebugUtil::RestoreState(const QDir& path)
{
QString restoreFile = path.absoluteFilePath(DEBUG_UTIL_XML);
try
{
Poco::XML::DOMParser parser;
Poco::FileInputStream reader(restoreFile.toStdString());
Poco::XML::InputSource source(reader);
//source.setSystemId(baseDir);
Poco::XML::Document* doc = parser.parse(&source);
Poco::XML::Element* debugutil = doc->documentElement();
if (debugutil)
{
// restore traced objects
Poco::XML::NodeList* elementList = debugutil->getElementsByTagName(TRACEOBJECT_TAG.toStdString());
for (std::size_t i = 0; i < elementList->length(); i++)
{
Poco::XML::Element* elem =
dynamic_cast<Poco::XML::Element*> (elementList->item(static_cast<unsigned long>(i)));
if (!elem->hasAttribute(ID_ATTR.toStdString())) continue;
const std::string& attr = elem->getAttribute(ID_ATTR.toStdString());
int traceId = 0;
try
{
traceId = Poco::NumberParser::parse(attr);
}
catch (const Poco::SyntaxException& e)
{
BERRY_WARN << e.displayText();
}
DebugUtil::TraceObject(traceId);
}
elementList->release();
// restore traced classes
elementList = debugutil->getElementsByTagName(TRACECLASS_TAG.toStdString());
for (std::size_t i = 0; i < elementList->length(); i++)
{
Poco::XML::Element* elem =
dynamic_cast<Poco::XML::Element*> (elementList->item(static_cast<unsigned long>(i)));
if (!elem->hasAttribute(NAME_ATTR.toStdString())) continue;
const std::string& traceClass = elem->getAttribute(NAME_ATTR.toStdString());
if (!traceClass.empty())
DebugUtil::TraceClass(QString::fromStdString(traceClass));
}
elementList->release();
}
doc->release();
}
catch (Poco::XML::SAXParseException& e)
{
BERRY_WARN << e.displayText();
}
catch (Poco::FileNotFoundException&)
{
}
catch (Poco::FileException& e)
{
BERRY_WARN << e.displayText();
}
// restore BreakpointManager
GetBreakpointManager()->RestoreState(path.absoluteFilePath(QString::fromStdString(DebugBreakpointManager::BREAKPOINTS_XML)));
}
}
diff --git a/Plugins/org.blueberry.ui.qt/src/internal/berryEditorHistoryItem.cpp b/Plugins/org.blueberry.ui.qt/src/internal/berryEditorHistoryItem.cpp
index 1ceffd3cf0..e259834687 100644
--- a/Plugins/org.blueberry.ui.qt/src/internal/berryEditorHistoryItem.cpp
+++ b/Plugins/org.blueberry.ui.qt/src/internal/berryEditorHistoryItem.cpp
@@ -1,211 +1,211 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "berryEditorHistoryItem.h"
#include "berryIEditorInput.h"
#include "berryIEditorDescriptor.h"
#include "berryIElementFactory.h"
#include "berryIMemento.h"
#include "berryIPersistableElement.h"
#include "berryStatus.h"
#include "berryWorkbenchConstants.h"
#include "berryWorkbenchPlugin.h"
namespace berry {
EditorHistoryItem::EditorHistoryItem(const SmartPointer<IEditorInput>& input,
const SmartPointer<IEditorDescriptor>& descriptor)
: input(input)
, descriptor(descriptor)
{
}
EditorHistoryItem::EditorHistoryItem(const SmartPointer<IMemento>& memento)
: memento(memento)
{
}
SmartPointer<IEditorDescriptor> EditorHistoryItem::GetDescriptor() const
{
return descriptor;
}
SmartPointer<IEditorInput> EditorHistoryItem::GetInput() const
{
return input;
}
bool EditorHistoryItem::IsRestored() const
{
return memento.IsNull();
}
QString EditorHistoryItem::GetName() const
{
QString result;
if (IsRestored() && GetInput().IsNotNull())
{
result = GetInput()->GetName();
}
else if (memento.IsNotNull())
{
memento->GetString(WorkbenchConstants::TAG_NAME, result);
}
return result;
}
QString EditorHistoryItem::GetToolTipText() const
{
QString result;
if (IsRestored() && GetInput().IsNotNull())
{
result = GetInput()->GetToolTipText();
}
else if (memento.IsNotNull())
{
memento->GetString(WorkbenchConstants::TAG_TOOLTIP, result);
}
return result;
}
bool EditorHistoryItem::Matches(const SmartPointer<IEditorInput>& input) const
{
if (IsRestored())
{
return input == GetInput();
}
// if not restored, compare name, tool tip text and factory id,
// avoiding as much work as possible
if (GetName() != input->GetName())
{
return false;
}
if (GetToolTipText() != input->GetToolTipText())
{
return false;
}
const IPersistableElement* persistable = input->GetPersistable();
- QString inputId = persistable ? QString::null : persistable->GetFactoryId();
+ QString inputId = persistable ? persistable->GetFactoryId() : QString::null;
QString myId = GetFactoryId();
return myId.isEmpty() ? inputId.isEmpty() : myId == inputId;
}
QString EditorHistoryItem::GetFactoryId() const
{
QString result;
if (IsRestored())
{
if (input.IsNotNull())
{
const IPersistableElement* persistable = input->GetPersistable();
if (persistable != nullptr)
{
result = persistable->GetFactoryId();
}
}
}
else if (memento.IsNotNull())
{
memento->GetString(WorkbenchConstants::TAG_FACTORY_ID, result);
}
return result;
}
SmartPointer<const IStatus> EditorHistoryItem::RestoreState()
{
Q_ASSERT_X(!IsRestored(), "RestoreState", "already restored");
IStatus::ConstPointer result = Status::OK_STATUS(BERRY_STATUS_LOC);
IMemento::Pointer memento = this->memento;
this->memento = nullptr;
QString factoryId;
memento->GetString(WorkbenchConstants::TAG_FACTORY_ID, factoryId);
if (factoryId.isEmpty())
{
WorkbenchPlugin::Log("Unable to restore mru list - no input factory ID.");
return result;
}
QScopedPointer<IElementFactory> factory(
PlatformUI::GetWorkbench()->GetElementFactory(factoryId));
if (!factory)
{
return result;
}
IMemento::Pointer persistableMemento = memento->GetChild(WorkbenchConstants::TAG_PERSISTABLE);
if (persistableMemento.IsNull())
{
WorkbenchPlugin::Log("Unable to restore mru list - no input element state: " + factoryId);
return result;
}
QScopedPointer<IAdaptable> adaptable(factory->CreateElement(persistableMemento));
if (adaptable == nullptr || dynamic_cast<IEditorInput*>(adaptable.data()) == nullptr)
{
return result;
}
input = dynamic_cast<IEditorInput*>(adaptable.data());
// Get the editor descriptor.
QString editorId;
memento->GetString(WorkbenchConstants::TAG_ID, editorId);
if (!editorId.isEmpty())
{
IEditorRegistry* registry = WorkbenchPlugin::GetDefault()->GetEditorRegistry();
descriptor = registry->FindEditor(editorId);
}
return result;
}
bool EditorHistoryItem::CanSave() const
{
return !IsRestored()
|| (GetInput().IsNotNull() && GetInput()->GetPersistable() != nullptr);
}
SmartPointer<const IStatus> EditorHistoryItem::SaveState(const SmartPointer<IMemento>& memento)
{
if (!IsRestored())
{
memento->PutMemento(this->memento);
}
else if (input.IsNotNull())
{
const IPersistableElement* persistable = input->GetPersistable();
if (persistable != nullptr)
{
/*
* Store IPersistable of the IEditorInput in a separate section
* since it could potentially use a tag already used in the parent
* memento and thus overwrite data.
*/
IMemento::Pointer persistableMemento = memento->CreateChild(WorkbenchConstants::TAG_PERSISTABLE);
persistable->SaveState(persistableMemento);
memento->PutString(WorkbenchConstants::TAG_FACTORY_ID,
persistable->GetFactoryId());
if (descriptor.IsNotNull() && !descriptor->GetId().isEmpty())
{
memento->PutString(WorkbenchConstants::TAG_ID, descriptor->GetId());
}
// save the name and tooltip separately so they can be restored
// without having to instantiate the input, which can activate plugins
memento->PutString(WorkbenchConstants::TAG_NAME, input->GetName());
memento->PutString(WorkbenchConstants::TAG_TOOLTIP, input->GetToolTipText());
}
}
return Status::OK_STATUS(BERRY_STATUS_LOC);
}
}
diff --git a/Plugins/org.blueberry.ui.qt/src/internal/berryEditorRegistry.h b/Plugins/org.blueberry.ui.qt/src/internal/berryEditorRegistry.h
index d0b9bedd82..3ac251dbc6 100644
--- a/Plugins/org.blueberry.ui.qt/src/internal/berryEditorRegistry.h
+++ b/Plugins/org.blueberry.ui.qt/src/internal/berryEditorRegistry.h
@@ -1,760 +1,754 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef BERRYEDITORREGISTRY_H_
#define BERRYEDITORREGISTRY_H_
#include <berryIExtensionPoint.h>
#include "berryIEditorRegistry.h"
#include "berryIFileEditorMapping.h"
#include "berryEditorDescriptor.h"
#include "berryFileEditorMapping.h"
namespace berry
{
/**
* \ingroup org_blueberry_ui_internal
*
* Provides access to the collection of defined editors for resource types.
*/
class EditorRegistry : public IEditorRegistry
{
class RelatedRegistry
{
public: RelatedRegistry(EditorRegistry* editorRegistry);
/**
* Return the objects related to the type.
*
* @param type
* @return the objects related to the type
*/
// public: QList<IEditorDescriptor::Pointer> GetRelatedObjects(IContentType type) {
// IEditorDescriptor[] relatedObjects = (IEditorDescriptor[]) contentTypeToEditorMappings.get(type);
// if (relatedObjects == null) {
// return EMPTY;
// }
// return (IEditorDescriptor[]) WorkbenchActivityHelper.restrictArray(relatedObjects);
// }
/**
* Return the objects related to the filename
* @param fileName
* @return the objects related to the filename
*/
public:
QList<IEditorDescriptor::Pointer> GetRelatedObjects(
const QString& fileName);
private: EditorRegistry* editorRegistry;
};
friend class RelatedRegistry;
/**
* Map of FileEditorMapping (extension to FileEditorMapping) Uses two
* java.util.HashMap: one keeps the default which are set by the plugins and
* the other keeps the changes made by the user through the preference page.
*/
class EditorMap
{
static QHash<QString, FileEditorMapping::Pointer> defaultMap;
static QHash<QString, FileEditorMapping::Pointer> map;
public: void Clear();
/**
* Put a default mapping into the editor map.
*
* @param key the key to set
* @param value the value to associate
*/
public: static void PutDefault(const QString& key, FileEditorMapping::Pointer value);
/**
* Put a mapping into the user editor map.
*
* @param key the key to set
* @param value the value to associate
*/
public: void Put(const QString& key, FileEditorMapping::Pointer value);
/**
* Return the mapping associated to the key. First searches user
* map, and then falls back to the default map if there is no match. May
* return <code>null</code>
*
* @param key
* the key to search for
* @return the mapping associated to the key or <code>null</code>
*/
public: FileEditorMapping::Pointer Get(const QString& key);
/**
* Return all mappings. This will return default mappings overlayed with
* user mappings.
*
* @return the mappings
*/
public: QList<FileEditorMapping::Pointer> AllMappings();
/**
* Return all user mappings.
*
* @return the mappings
*/
public: QList<FileEditorMapping::Pointer> UserMappings();
};
- struct CmpFileEditorMapping : public std::binary_function<FileEditorMapping::Pointer,
- FileEditorMapping::Pointer,
- bool>
+ struct CmpFileEditorMapping
{
bool operator()(const FileEditorMapping::Pointer& x, const FileEditorMapping::Pointer& y) const
{
return x->GetLabel() < y->GetLabel();
}
};
- struct CmpIEditorDescriptor : public std::binary_function<IEditorDescriptor::Pointer,
- IEditorDescriptor::Pointer,
- bool>
+ struct CmpIEditorDescriptor
{
bool operator()(const IEditorDescriptor::Pointer& x, const IEditorDescriptor::Pointer& y) const
{
return x->GetLabel() < y->GetLabel();
}
};
- struct CmpEditorDescriptor : public std::binary_function<EditorDescriptor::Pointer,
- EditorDescriptor::Pointer,
- bool>
+ struct CmpEditorDescriptor
{
bool operator()(const EditorDescriptor::Pointer& x, const EditorDescriptor::Pointer& y) const
{
return x->GetLabel() < y->GetLabel();
}
};
//private: Map contentTypeToEditorMappings = new HashMap();
/*
* Cached images - these include images from registered editors (via
* plugins) and others hence this table is not one to one with the mappings
* table. It is in fact a superset of the keys one would find in
* typeEditorMappings
*/
//private: Map extensionImages = new HashMap();
/**
* Vector of EditorDescriptor - all the editors loaded from plugin files.
* The list is kept in order to be able to show in the editor selection
* dialog of the resource associations page. This list is sorted based on the
* human readable label of the editor descriptor.
*
* @see #comparer
*/
private:
QList<EditorDescriptor::Pointer> sortedEditorsFromPlugins;
// Map of EditorDescriptor - map editor id to editor.
private:
QHash<QString, EditorDescriptor::Pointer> mapIDtoEditor;
// Map of FileEditorMapping (extension to FileEditorMapping)
private:
EditorMap typeEditorMappings;
/*
* Compares the labels from two IEditorDescriptor objects
*/
//private:
// static final Comparator comparer = new Comparator()
// {
// private Collator collator = Collator.getInstance();
//
// public int compare(Object arg0, Object arg1)
// {
// String s1 = ((IEditorDescriptor) arg0).getLabel();
// String s2 = ((IEditorDescriptor) arg1).getLabel();
// return collator.compare(s1, s2);
// }
// };
private: RelatedRegistry relatedRegistry;
public: static const QString EMPTY_EDITOR_ID; // = "org.blueberry.ui.internal.emptyEditorTab"
/**
* Return an instance of the receiver. Adds listeners into the extension
* registry for dynamic UI purposes.
*/
public: EditorRegistry();
/**
* Add an editor for the given extensions with the specified (possibly null)
* extended type. The editor is being registered from a plugin
*
* @param editor
* The description of the editor (as obtained from the plugin
* file and built by the registry reader)
* @param extensions
* Collection of file extensions the editor applies to
* @param filenames
* Collection of filenames the editor applies to
* @param contentTypeVector
* @param bDefault
* Indicates whether the editor should be made the default editor
* and hence appear first inside a FileEditorMapping
*
* This method is not API and should not be called outside the workbench
* code.
*/
public: void AddEditorFromPlugin(EditorDescriptor::Pointer editor, const QList<QString>& extensions,
const QList<QString>& filenames,
const QList<QString>& contentTypeVector,
bool bDefault);
/**
* Add external editors to the editor mapping.
*/
private: void AddExternalEditorsToEditorMap();
/*
* (non-Javadoc) Method declared on IEditorRegistry.
*/
//public: void AddPropertyListener(IPropertyListener l) {
// addListenerObject(l);
// }
/*
* (non-Javadoc) Method declared on IEditorRegistry.
*/
public: IEditorDescriptor::Pointer FindEditor(const QString& id) override;
/**
* Fires a property changed event to all registered listeners.
*
* @param type the type of event
* @see IEditorRegistry#PROP_CONTENTS
*/
// private: void FirePropertyChange(final int type) {
// Object[] array = getListeners();
// for (int nX = 0; nX < array.length; nX++) {
// final IPropertyListener l = (IPropertyListener) array[nX];
// Platform.run(new SafeRunnable() {
// public: void run() {
// l.propertyChanged(EditorRegistry.this, type);
// }
// });
// }
// }
/*
* (non-Javadoc) Method declared on IEditorRegistry.
*
* @deprecated
*/
public: IEditorDescriptor::Pointer GetDefaultEditor() override;
/*
* (non-Javadoc) Method declared on IEditorRegistry.
*/
public: IEditorDescriptor::Pointer GetDefaultEditor(const QString& filename) override;
/**
* Return the (approximated) content type for a file with the given name.
*
* @param filename the filename
* @return the content type or <code>null</code> if it could not be determined
* @since 3.1
*/
// private: IContentType::Pointer GuessAtContentType(const QString& filename) {
// return Platform.getContentTypeManager().findContentTypeFor(filename);
// }
/**
* Returns the default file image descriptor.
*
* @return the image descriptor
*/
// private: ImageDescriptor GetDefaultImage() {
// // @issue what should be the default image?
// return WorkbenchImages.getImageDescriptor(ISharedImages.IMG_OBJ_FILE);
// }
/*
* (non-Javadoc) Method declared on IEditorRegistry.
*/
public: QList<IEditorDescriptor::Pointer> GetEditors(const QString& filename) override;
/*
* (non-Javadoc) Method declared on IEditorRegistry.
*/
public: QList<IFileEditorMapping::Pointer> GetFileEditorMappings() override;
/*
* (non-Javadoc) Method declared on IEditorRegistry.
*/
// public: ImageDescriptor GetImageDescriptor(String filename) {
// return getImageDescriptor(filename, guessAtContentType(filename));
// }
/**
* Find the file editor mapping for the file extension. Returns
* <code>null</code> if not found.
*
* @param ext
* the file extension
* @return the mapping, or <code>null</code>
*/
private: FileEditorMapping::Pointer GetMappingFor(const QString& ext);
/**
* Find the file editor mappings for the given filename.
* <p>
* Return an array of two FileEditorMapping items, where the first mapping
* is for the entire filename, and the second mapping is for the filename's
* extension only. These items can be null if no mapping exist on the
* filename and/or filename's extension.</p>
*
* @param filename the filename
* @return the mappings
*/
private: QList<FileEditorMapping::Pointer> GetMappingForFilename(const QString& filename);
/**
* Return the editor descriptors pulled from the OS.
* <p>
* WARNING! The image described by each editor descriptor is *not* known by
* the workbench's graphic registry. Therefore clients must take care to
* ensure that if they access any of the images held by these editors that
* they also dispose them
* </p>
* @return the editor descriptors
*/
//public: QList<IEditorDescriptor::Pointer> GetSortedEditorsFromOS();
/**
* Return the editors loaded from plugins.
*
* @return the sorted array of editors declared in plugins
* @see #comparer
*/
public: QList<IEditorDescriptor::Pointer> GetSortedEditorsFromPlugins();
/**
* Answer an intial id to editor map. This will create a new map and
* populate it with the default system editors.
*
* @param initialSize
* the initial size of the map
* @return the new map
*/
private: void InitialIdToEditorMap(QHash<QString, EditorDescriptor::Pointer>& map);
/**
* Add the system editors to the provided map. This will always add an
* editor with an id of {@link #SYSTEM_EXTERNAL_EDITOR_ID} and may also add
* an editor with id of {@link #SYSTEM_INPLACE_EDITOR_ID} if the system
* configuration supports it.
*
* @param map the map to augment
*/
private: void AddSystemEditors(QHash<QString, EditorDescriptor::Pointer>& map);
/**
* Initialize the registry state from plugin declarations and preference
* overrides.
*/
private: void InitializeFromStorage();
/**
* Set the default editors according to the preference store which can be
* overwritten in the file properties.ini. In the form:
* <p>
* <code>ext1:id1;ext2:id2;...</code>
* </p>
*
* @param defaultEditors the default editors to set
*/
private: void SetProductDefaults(const QString& defaultEditors);
/**
* Read the editors defined in the preferences store.
*
* @param editorTable
* Editor table to store the editor definitions.
* @return true if the table is built succesfully.
*/
private: bool ReadEditors(QHash<QString, EditorDescriptor::Pointer>& editorTable);
/**
* Read the file types and associate them to their defined editor(s).
*
* @param editorTable
* The editor table containing the defined editors.
* @param reader
* Reader containing the preferences content for the resources.
*
* @throws WorkbenchException
*/
public: void ReadResources(QHash<QString, EditorDescriptor::Pointer>& editorTable, std::ostream& reader);
/**
* Determine if the editors list contains the editor descriptor.
*
* @param editorsArray
* The list of editors
* @param editorDescriptor
* The editor descriptor
* @return <code>true</code> if the editors list contains the editor descriptor
*/
private: bool Contains(const QList<IEditorDescriptor::Pointer>& editorsArray,
IEditorDescriptor::Pointer editorDescriptor);
/**
* Creates the reader for the resources preferences defined in the
* preference store.
*
* @param editorTable
* The editor table containing the defined editors.
* @return true if the resources are read succesfully.
*/
private: bool ReadResources(QHash<QString, EditorDescriptor::Pointer>& editorTable);
/**
* Load the serialized resource associations Return true if the operation
* was successful, false otherwise
*/
private: bool LoadAssociations();
/**
* Return a friendly version of the given key suitable for use in the editor
* map.
*/
private: QString MappingKeyFor(const QString& type);
/**
* Return a key that combines the file's name and extension of the given
* mapping
*
* @param mapping the mapping to generate a key for
*/
private: QString MappingKeyFor(FileEditorMapping::Pointer mapping);
/**
* Rebuild the editor map
*/
private: void RebuildEditorMap();
/**
* Rebuild the internal editor mapping.
*/
private: void RebuildInternalEditorMap();
/*
* (non-Javadoc) Method declared on IEditorRegistry.
*/
// public: void RemovePropertyListener(IPropertyListener l) {
// removeListenerObject(l);
// }
/**
* Save the registry to the filesystem by serializing the current resource
* associations.
*/
public: void SaveAssociations();
/**
* Set the collection of FileEditorMappings. The given collection is
* converted into the internal hash table for faster lookup Each mapping
* goes from an extension to the collection of editors that work on it. This
* operation will rebuild the internal editor mappings.
*
* @param newResourceTypes
* te new file editor mappings.
*/
public: void SetFileEditorMappings(const QList<FileEditorMapping::Pointer>& newResourceTypes);
/*
* (non-Javadoc) Method declared on IEditorRegistry.
*/
public: void SetDefaultEditor(const QString& fileName, const QString& editorId) override;
/**
* Alphabetically sort the internal editors.
*
* @see #comparer
*/
private: QList<IEditorDescriptor::Pointer> SortEditors(const QList<IEditorDescriptor::Pointer>& unsortedList);
/**
* Alphabetically sort the internal editors.
*
* @see #comparer
*/
private: void SortInternalEditors();
/*
* (non-Javadoc)
*
* @see org.blueberry.ui.IEditorRegistry#isSystemInPlaceEditorAvailable(String)
*/
public: bool IsSystemInPlaceEditorAvailable(const QString& filename) override;
/*
* (non-Javadoc)
*
* @see org.blueberry.ui.IEditorRegistry#isSystemExternalEditorAvailable(String)
*/
public: bool IsSystemExternalEditorAvailable(const QString& filename) override;
/*
* (non-Javadoc)
*
* @see org.blueberry.ui.IEditorRegistry#getSystemExternalEditorImageDescriptor(java.lang.String)
*/
// public: ImageDescriptor GetSystemExternalEditorImageDescriptor(
// const QString& filename) {
// Program externalProgram = null;
// int extensionIndex = filename.lastIndexOf('.');
// if (extensionIndex >= 0) {
// externalProgram = Program.findProgram(filename
// .substring(extensionIndex));
// }
// if (externalProgram == null) {
// return null;
// }
//
// return new ExternalProgramImageDescriptor(externalProgram);
// }
/**
* Removes the entry with the value of the editor descriptor from the given
* map. If the descriptor is the last descriptor in a given
* FileEditorMapping then the mapping is removed from the map.
*
* @param map
* the map to search
* @param desc
* the descriptor value to remove
*/
private: void RemoveEditorFromMapping(QHash<QString, FileEditorMapping::Pointer>& map, IEditorDescriptor::Pointer desc);
private: IExtensionPoint::Pointer GetExtensionPointFilter();
/* (non-Javadoc)
* @see org.blueberry.ui.IEditorRegistry#getDefaultEditor(java.lang.String, org.blueberry.core.runtime.content.IContentType)
*/
// public: IEditorDescriptor::Pointer GetDefaultEditor(const QString& fileName, IContentType contentType) {
// return getEditorForContentType(fileName, contentType);
// }
/**
* Return the editor for a file with a given content type.
*
* @param filename the file name
* @param contentType the content type
* @return the editor for a file with a given content type
* @since 3.1
*/
private: IEditorDescriptor::Pointer GetEditorForContentType(const QString& filename
/*IContentType contentType*/);
/* (non-Javadoc)
* @see org.blueberry.ui.IEditorRegistry#getEditors(java.lang.String, org.blueberry.core.runtime.content.IContentType)
*/
// public: QList<IEditorDescriptor::Pointer> GetEditors(const QString& fileName, IContentType contentType) {
// return findRelatedObjects(contentType, fileName, relatedRegistry);
// }
/* (non-Javadoc)
* @see org.blueberry.ui.IEditorRegistry#getImageDescriptor(java.lang.String, org.blueberry.core.runtime.content.IContentType)
*/
// public: ImageDescriptor GetImageDescriptor(const QString filename, IContentType contentType) {
// if (filename == null) {
// return getDefaultImage();
// }
//
// if (contentType != null) {
// IEditorDescriptor desc = getEditorForContentType(filename, contentType);
// if (desc != null) {
// ImageDescriptor anImage = (ImageDescriptor) extensionImages.get(desc);
// if (anImage != null) {
// return anImage;
// }
// anImage = desc.getImageDescriptor();
// extensionImages.put(desc, anImage);
// return anImage;
// }
// }
// // Lookup in the cache first...
// String key = mappingKeyFor(filename);
// ImageDescriptor anImage = (ImageDescriptor) extensionImages.get(key);
// if (anImage != null) {
// return anImage;
// }
//
// // See if we have a mapping for the filename or extension
// FileEditorMapping[] mapping = getMappingForFilename(filename);
// for (int i = 0; i < 2; i++) {
// if (mapping[i] != null) {
// // Lookup in the cache first...
// String mappingKey = mappingKeyFor(mapping[i]);
// ImageDescriptor mappingImage = (ImageDescriptor) extensionImages
// .get(key);
// if (mappingImage != null) {
// return mappingImage;
// }
// // Create it and cache it
// IEditorDescriptor editor = mapping[i].getDefaultEditor();
// if (editor != null) {
// mappingImage = editor.getImageDescriptor();
// extensionImages.put(mappingKey, mappingImage);
// return mappingImage;
// }
// }
// }
//
// // Nothing - time to look externally for the icon
// anImage = getSystemExternalEditorImageDescriptor(filename);
// if (anImage == null) {
// anImage = getDefaultImage();
// }
// // for dynamic UI - comment out the next line
// //extensionImages.put(key, anImage);
// return anImage;
//
// }
/**
* Find objects related to the content type.
*
* This method is temporary and exists only to back us off of the
* soon-to-be-removed IContentTypeManager.IRelatedRegistry API.
*
* @param type
* @param fileName
* @param registry
* @return the related objects
*/
private: QList<IEditorDescriptor::Pointer> FindRelatedObjects(/*IContentType type,*/ const QString& fileName,
RelatedRegistry& registry);
/**
* Return the editors bound to this content type, either directly or indirectly.
*
* @param type the content type to check
* @return the editors
* @since 3.1
*
* TODO: this should be rolled in with the above findRelatedObjects code
*/
// public: QList<IEditorDescriptor> GetEditorsForContentType(IContentType type) {
// ArrayList allRelated = new ArrayList();
// if (type == null) {
// return new IEditorDescriptor [0];
// }
//
// Object [] related = relatedRegistry.getRelatedObjects(type);
// for (int i = 0; i < related.length; i++) {
// // we don't want to return duplicates
// if (!allRelated.contains(related[i])) {
// // if it's not filtered, add it to the list
// if (!WorkbenchActivityHelper.filterItem(related[i])) {
// allRelated.add(related[i]);
// }
//
// }
// }
//
// // now add any indirectly related objects, walking up the content type hierarchy
// while ((type = type.getBaseType()) != null) {
// related = relatedRegistry.getRelatedObjects(type);
// for (int i = 0; i < related.length; i++) {
// // we don't want to return duplicates
// if (!allRelated.contains(related[i])) {
// // if it's not filtered, add it to the list
// if (!WorkbenchActivityHelper.filterItem(related[i])) {
// allRelated.add(related[i]);
// }
// }
// }
// }
//
// return (IEditorDescriptor[]) allRelated.toArray(new IEditorDescriptor[allRelated.size()]);
// }
/**
* Get filemappings for all defined filetypes, including those defined by content type.
*
* @return the filetypes
* @since 3.1
*/
public: QList<IFileEditorMapping::Pointer> GetUnifiedMappings();
};
class MockMapping : public IFileEditorMapping
{
//private: IContentType contentType;
private:
QString extension;
private:
QString filename;
MockMapping(/*IContentType type,*/const QString& name,
const QString& ext);
public:
IEditorDescriptor::Pointer GetDefaultEditor() override;
public:
QList<IEditorDescriptor::Pointer> GetEditors() const override;
public:
QList<IEditorDescriptor::Pointer> GetDeletedEditors() const override;
public:
QString GetExtension() const override;
// public: ImageDescriptor GetImageDescriptor() {
// IEditorDescriptor editor = getDefaultEditor();
// if (editor == null) {
// return WorkbenchImages
// .getImageDescriptor(ISharedImages.IMG_OBJ_FILE);
// }
//
// return editor.getImageDescriptor();
// }
public:
QString GetLabel() const override;
public:
QString GetName() const override;
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
public:
bool operator==(const Object* obj) const override;
};
}
#endif /*BERRYEDITORREGISTRY_H_*/
diff --git a/Plugins/org.blueberry.ui.qt/src/internal/berryPerspective.cpp b/Plugins/org.blueberry.ui.qt/src/internal/berryPerspective.cpp
index 62250388bb..ae868a8d1f 100644
--- a/Plugins/org.blueberry.ui.qt/src/internal/berryPerspective.cpp
+++ b/Plugins/org.blueberry.ui.qt/src/internal/berryPerspective.cpp
@@ -1,1809 +1,1807 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "tweaklets/berryGuiWidgetsTweaklet.h"
#include "berryPerspective.h"
#include "berryPerspectiveHelper.h"
#include "berryWorkbenchPlugin.h"
#include "berryWorkbenchConstants.h"
#include "berryPerspectiveExtensionReader.h"
#include "berryEditorSashContainer.h"
#include "berryPartSite.h"
#include "berryViewSite.h"
#include "berryEditorAreaHelper.h"
#include "intro/berryIntroConstants.h"
#include "berryWorkbenchWindow.h"
#include "berryStatusUtil.h"
#include "berryMultiStatus.h"
#include "berryXMLMemento.h"
#include "presentations/berryIStackPresentationSite.h"
#include "berryIContextService.h"
#include <QMessageBox>
namespace berry
{
const QString Perspective::VERSION_STRING = "0.016";
Perspective::Perspective(PerspectiveDescriptor::Pointer desc,
WorkbenchPage::Pointer page)
: descriptor(desc)
{
this->Register();
this->Init(page);
if (desc.IsNotNull())
{
this->CreatePresentation(desc);
}
this->UnRegister(false);
}
Perspective::Perspective(WorkbenchPage::Pointer page)
{
this->Init(page);
}
void Perspective::Init(WorkbenchPage::Pointer page)
{
editorHidden = false;
editorAreaState = IStackPresentationSite::STATE_RESTORED;
fixed = false;
presentation = nullptr;
shouldHideEditorsOnActivate = false;
this->page = page.GetPointer();
this->editorArea = page->GetEditorPresentation()->GetLayoutPart();
this->viewFactory = page->GetViewFactory();
}
bool Perspective::BringToTop(IViewReference::Pointer ref)
{
return presentation->BringPartToTop(this->GetPane(ref));
}
bool Perspective::ContainsView(IViewPart::Pointer view)
{
IViewSite::Pointer site = view->GetViewSite();
IViewReference::Pointer ref = this->FindView(site->GetId(), site->GetSecondaryId());
if (ref.IsNull())
{
return false;
}
return (view.Cast<IWorkbenchPart>() == ref->GetPart(false));
}
bool Perspective::ContainsView(const QString& viewId) const
{
return mapIDtoViewLayoutRec.contains(viewId);
}
void Perspective::CreatePresentation(PerspectiveDescriptor::Pointer persp)
{
if (persp->HasCustomDefinition())
{
this->LoadCustomPersp(persp);
}
else
{
this->LoadPredefinedPersp(persp);
}
}
Perspective::~Perspective()
{
// Get rid of presentation.
if (presentation == nullptr)
{
DisposeViewRefs();
return;
}
presentation->Deactivate();
// Release each view.
QList<IViewReference::Pointer> refs(this->GetViewReferences());
for (auto & ref : refs)
{
this->GetViewFactory()->ReleaseView(ref);
}
mapIDtoViewLayoutRec.clear();
delete presentation;
}
void Perspective::DisposeViewRefs()
{
if (!memento)
{
return;
}
QList<IMemento::Pointer> views(memento->GetChildren(WorkbenchConstants::TAG_VIEW));
for (int x = 0; x < views.size(); x++)
{
// Get the view details.
IMemento::Pointer childMem = views[x];
QString id; childMem->GetString(WorkbenchConstants::TAG_ID, id);
// skip creation of the intro reference - it's handled elsewhere.
if (id == IntroConstants::INTRO_VIEW_ID)
{
continue;
}
QString secondaryId = ViewFactory::ExtractSecondaryId(id);
if (!secondaryId.isEmpty())
{
id = ViewFactory::ExtractPrimaryId(id);
}
QString removed;
childMem->GetString(WorkbenchConstants::TAG_REMOVED, removed);
if (removed != "true")
{
IViewReference::Pointer ref = viewFactory->GetView(id, secondaryId);
if (ref)
{
viewFactory->ReleaseView(ref);
}
}
}
}
IViewReference::Pointer Perspective::FindView(const QString& viewId)
{
return this->FindView(viewId, "");
}
IViewReference::Pointer Perspective::FindView(const QString& id, const QString& secondaryId)
{
QList<IViewReference::Pointer> refs(this->GetViewReferences());
for (int i = 0; i < refs.size(); i++)
{
IViewReference::Pointer ref = refs[i];
if (id == ref->GetId()
&& (secondaryId == ref->GetSecondaryId()))
{
return ref;
}
}
return IViewReference::Pointer(nullptr);
}
QWidget* Perspective::GetClientComposite()
{
return page->GetClientComposite();
}
IPerspectiveDescriptor::Pointer Perspective::GetDesc()
{
return descriptor;
}
PartPane::Pointer Perspective::GetPane(IViewReference::Pointer ref)
{
return ref.Cast<WorkbenchPartReference>()->GetPane();
}
QList<QString> Perspective::GetPerspectiveShortcuts()
{
return perspectiveShortcuts;
}
PerspectiveHelper* Perspective::GetPresentation() const
{
return presentation;
}
QList<QString> Perspective::GetShowViewShortcuts()
{
return showViewShortcuts;
}
ViewFactory* Perspective::GetViewFactory()
{
return viewFactory;
}
QList<IViewReference::Pointer> Perspective::GetViewReferences()
{
// Get normal views.
if (presentation == nullptr)
{
return QList<IViewReference::Pointer>();
}
QList<PartPane::Pointer> panes;
presentation->CollectViewPanes(panes);
QList<IViewReference::Pointer> result;
// List fastViews = (fastViewManager != 0) ?
// fastViewManager.getFastViews(0)
// : new ArrayList();
// IViewReference[] resultArray = new IViewReference[panes.size()
// + fastViews.size()];
//
// // Copy fast views.
// int nView = 0;
// for (int i = 0; i < fastViews.size(); i++)
// {
// resultArray[nView] = (IViewReference) fastViews.get(i);
// ++nView;
// }
// Copy normal views.
for (QList<PartPane::Pointer>::iterator iter = panes.begin();
iter != panes.end(); ++iter)
{
PartPane::Pointer pane = *iter;
result.push_back(pane->GetPartReference().Cast<IViewReference>());
}
return result;
}
void Perspective::HideEditorArea()
{
if (!this->IsEditorAreaVisible())
{
return;
}
// Show the editor in the appropriate location
if (this->UseNewMinMax(Perspective::Pointer(this)))
{
// If it's the currently maximized part we have to restore first
// if (this->GetPresentation().getMaximizedStack().Cast<EditorStack>() != 0)
// {
// getPresentation().getMaximizedStack().setState(IStackPresentationSite.STATE_RESTORED);
// }
bool isMinimized = editorAreaState == IStackPresentationSite::STATE_MINIMIZED;
if (!isMinimized)
this->HideEditorAreaLocal();
//else
// this->SetEditorAreaTrimVisibility(false);
}
else
{
this->HideEditorAreaLocal();
}
editorHidden = true;
}
void Perspective::HideEditorAreaLocal()
{
if (editorHolder != 0)
{
return;
}
// Replace the editor area with a placeholder so we
// know where to put it back on show editor area request.
editorHolder = new PartPlaceholder(editorArea->GetID());
presentation->GetLayout()->Replace(editorArea, editorHolder);
}
bool Perspective::HideView(IViewReference::Pointer ref)
{
// If the view is locked just return.
PartPane::Pointer pane = this->GetPane(ref);
presentation->RemovePart(pane);
// Dispose view if ref count == 0.
this->GetViewFactory()->ReleaseView(ref);
return true;
}
bool Perspective::IsEditorAreaVisible()
{
return !editorHidden;
}
ViewLayoutRec::Pointer Perspective::GetViewLayoutRec(IViewReference::Pointer ref, bool create)
{
ViewLayoutRec::Pointer result = this->GetViewLayoutRec(ViewFactory::GetKey(ref), create);
if (result.IsNull() && create==false)
{
result = this->GetViewLayoutRec(ref->GetId(), false);
}
return result;
}
ViewLayoutRec::Pointer Perspective::GetViewLayoutRec(const QString& viewId, bool create)
{
ViewLayoutRec::Pointer rec = mapIDtoViewLayoutRec[viewId];
if (rec.IsNull() && create)
{
rec = new ViewLayoutRec();
mapIDtoViewLayoutRec[viewId] = rec;
}
return rec;
}
bool Perspective::IsFixedLayout()
{
//@issue is there a difference between a fixed
//layout and a fixed perspective?? If not the API
//may need some polish, WorkbenchPage, PageLayout
//and Perspective all have isFixed methods.
//PageLayout and Perspective have their own fixed
//attribute, we are assuming they are always in sync.
//WorkbenchPage delegates to the perspective.
return fixed;
}
bool Perspective::IsStandaloneView(IViewReference::Pointer ref)
{
ViewLayoutRec::Pointer rec = this->GetViewLayoutRec(ref, false);
return rec.IsNotNull() && rec->isStandalone;
}
bool Perspective::GetShowTitleView(IViewReference::Pointer ref)
{
ViewLayoutRec::Pointer rec = this->GetViewLayoutRec(ref, false);
return rec.IsNotNull() && rec->showTitle;
}
void Perspective::LoadCustomPersp(PerspectiveDescriptor::Pointer persp)
{
//get the layout from the registry
PerspectiveRegistry* perspRegistry = dynamic_cast<PerspectiveRegistry*>(WorkbenchPlugin::GetDefault()->GetPerspectiveRegistry());
try
{
IMemento::Pointer memento = perspRegistry->GetCustomPersp(persp->GetId());
// Restore the layout state.
// MultiStatus status = new MultiStatus(
// PlatformUI.PLUGIN_ID,
// IStatus.OK,
// NLS.bind(WorkbenchMessages.Perspective_unableToRestorePerspective, persp.getLabel()),
// 0);
// status.merge(restoreState(memento));
// status.merge(restoreState());
bool okay = true;
okay &= this->RestoreState(memento);
okay &= this->RestoreState();
if (!okay)
{
this->UnableToOpenPerspective(persp, "Unable to open perspective: " + persp->GetLabel());
}
}
//catch (IOException e)
//{
// unableToOpenPerspective(persp, 0);
//}
catch (const WorkbenchException& e)
{
this->UnableToOpenPerspective(persp, e.what());
}
}
void Perspective::UnableToOpenPerspective(PerspectiveDescriptor::Pointer persp,
const QString& status)
{
PerspectiveRegistry* perspRegistry = dynamic_cast<PerspectiveRegistry*>(WorkbenchPlugin
::GetDefault()->GetPerspectiveRegistry());
perspRegistry->DeletePerspective(persp);
// If this is a predefined perspective, we will not be able to delete
// the perspective (we wouldn't want to). But make sure to delete the
// customized portion.
persp->DeleteCustomDefinition();
QString title = "Restoring problems";
QString msg = "Unable to read workbench state.";
if (status == "")
{
QMessageBox::critical(nullptr, title, msg);
}
else
{
//TODO error dialog
//ErrorDialog.openError((Shell) 0, title, msg, status);
QMessageBox::critical(nullptr, title, msg + "\n" + status);
}
}
void Perspective::LoadPredefinedPersp(PerspectiveDescriptor::Pointer persp)
{
// Create layout engine.
IPerspectiveFactory::Pointer factory;
try
{
factory = persp->CreateFactory();
}
catch (CoreException& /*e*/)
{
throw WorkbenchException("Unable to load perspective: " + persp->GetId());
}
/*
* IPerspectiveFactory#createFactory() can return 0
*/
if (factory == 0)
{
throw WorkbenchException("Unable to load perspective: " + persp->GetId());
}
// Create layout factory.
ViewSashContainer::Pointer container(new ViewSashContainer(page, this->GetClientComposite()));
layout = new PageLayout(container, this->GetViewFactory(),
editorArea, descriptor);
layout->SetFixed(descriptor->GetFixed());
// // add the placeholders for the sticky folders and their contents
IPlaceholderFolderLayout::Pointer stickyFolderRight, stickyFolderLeft, stickyFolderTop, stickyFolderBottom;
QList<IStickyViewDescriptor::Pointer> descs(WorkbenchPlugin::GetDefault()
->GetViewRegistry()->GetStickyViews());
for (int i = 0; i < descs.size(); i++)
{
IStickyViewDescriptor::Pointer stickyViewDescriptor = descs[i];
QString id = stickyViewDescriptor->GetId();
int location = stickyViewDescriptor->GetLocation();
if (location == IPageLayout::RIGHT)
{
if (stickyFolderRight == 0)
{
stickyFolderRight = layout
->CreatePlaceholderFolder(
StickyViewDescriptor::STICKY_FOLDER_RIGHT,
IPageLayout::RIGHT, .75f,
IPageLayout::ID_EDITOR_AREA);
}
stickyFolderRight->AddPlaceholder(id);
}
else if (location == IPageLayout::LEFT)
{
if (stickyFolderLeft == 0)
{
stickyFolderLeft = layout->CreatePlaceholderFolder(
StickyViewDescriptor::STICKY_FOLDER_LEFT,
IPageLayout::LEFT, .25f, IPageLayout::ID_EDITOR_AREA);
}
stickyFolderLeft->AddPlaceholder(id);
}
else if (location == IPageLayout::TOP)
{
if (stickyFolderTop == 0)
{
stickyFolderTop = layout->CreatePlaceholderFolder(
StickyViewDescriptor::STICKY_FOLDER_TOP,
IPageLayout::TOP, .25f, IPageLayout::ID_EDITOR_AREA);
}
stickyFolderTop->AddPlaceholder(id);
}
else if (location == IPageLayout::BOTTOM)
{
if (stickyFolderBottom == 0)
{
stickyFolderBottom = layout->CreatePlaceholderFolder(
StickyViewDescriptor::STICKY_FOLDER_BOTTOM,
IPageLayout::BOTTOM, .75f,
IPageLayout::ID_EDITOR_AREA);
}
stickyFolderBottom->AddPlaceholder(id);
}
//should never be 0 as we've just added the view above
IViewLayout::Pointer viewLayout = layout->GetViewLayout(id);
viewLayout->SetCloseable(stickyViewDescriptor->IsCloseable());
viewLayout->SetMoveable(stickyViewDescriptor->IsMoveable());
}
// Run layout engine.
factory->CreateInitialLayout(layout);
PerspectiveExtensionReader extender;
extender.ExtendLayout(page->GetExtensionTracker(), descriptor->GetId(), layout);
// Retrieve view layout info stored in the page layout.
QHash<QString, ViewLayoutRec::Pointer> layoutInfo = layout->GetIDtoViewLayoutRecMap();
mapIDtoViewLayoutRec.unite(layoutInfo);
//TODO Perspective action sets
// Create action sets.
//List temp = new ArrayList();
//this->CreateInitialActionSets(temp, layout.getActionSets());
// IContextService* service = 0;
// if (page != 0)
// {
// service = page->GetWorkbenchWindow()->GetService<IContextService>();
// }
// try
// {
// if (service != 0)
// {
// service->DeferUpdates(true);
// }
// for (Iterator iter = temp.iterator(); iter.hasNext();)
// {
// IActionSetDescriptor descriptor = (IActionSetDescriptor) iter
// .next();
// addAlwaysOn(descriptor);
// }
// }finally
// {
// if (service!=0)
// {
// service.activateContext(ContextAuthority.SEND_EVENTS);
// }
// }
// newWizardShortcuts = layout.getNewWizardShortcuts();
showViewShortcuts = layout->GetShowViewShortcuts();
perspectiveShortcuts = layout->GetPerspectiveShortcuts();
showInPartIds = layout->GetShowInPartIds();
// // Retrieve fast views
// if (fastViewManager != 0)
// {
// ArrayList fastViews = layout.getFastViews();
// for (Iterator fvIter = fastViews.iterator(); fvIter.hasNext();)
// {
// IViewReference ref = (IViewReference) fvIter.next();
// fastViewManager.addViewReference(FastViewBar.FASTVIEWBAR_ID, -1, ref,
// !fvIter.hasNext());
// }
// }
// Is the layout fixed
fixed = layout->IsFixed();
showViewShortcuts = layout->GetShowViewShortcuts();
// Create presentation.
presentation = new PerspectiveHelper(page, container, this);
// Hide editor area if requested by factory
if (!layout->IsEditorAreaVisible())
{
this->HideEditorArea();
}
}
void Perspective::OnActivate()
{
// Update editor area state.
if (editorArea->GetControl() != nullptr)
{
bool visible = this->IsEditorAreaVisible();
bool inTrim = editorAreaState == IStackPresentationSite::STATE_MINIMIZED;
editorArea->SetVisible(visible && !inTrim);
}
// // Update fast views.
// // Make sure the control for the fastviews are created so they can
// // be activated.
// if (fastViewManager != 0)
// {
// List fastViews = fastViewManager.getFastViews(0);
// for (int i = 0; i < fastViews.size(); i++)
// {
// ViewPane pane = getPane((IViewReference) fastViews.get(i));
// if (pane != 0)
// {
// Control ctrl = pane.getControl();
// if (ctrl == 0)
// {
// pane.createControl(getClientComposite());
// ctrl = pane.getControl();
// }
// ctrl.setEnabled(false); // Remove focus support.
// }
// }
// }
// // Set the visibility of all fast view pins
// setAllPinsVisible(true);
// Trim Stack Support
bool useNewMinMax = Perspective::UseNewMinMax(Perspective::Pointer(this));
bool hideEditorArea = shouldHideEditorsOnActivate || (editorHidden && editorHolder == 0);
// We have to set the editor area's stack state -before-
// activating the presentation since it's used there to determine
// size of the resulting stack
if (useNewMinMax && !hideEditorArea)
{
this->RefreshEditorAreaVisibility();
}
// Show the layout
presentation->Activate(this->GetClientComposite());
// if (useNewMinMax)
// {
// fastViewManager.activate();
//
// // Move any minimized extension stacks to the trim
// if (layout != 0)
// {
// // Turn aimations off
// IPreferenceStore preferenceStore = PrefUtil.getAPIPreferenceStore();
// bool useAnimations = preferenceStore
// .getbool(IWorkbenchPreferenceConstants.ENABLE_ANIMATIONS);
// preferenceStore.setValue(IWorkbenchPreferenceConstants.ENABLE_ANIMATIONS, false);
//
// List minStacks = layout.getMinimizedStacks();
// for (Iterator msIter = minStacks.iterator(); msIter.hasNext();)
// {
// ViewStack vs = (ViewStack) msIter.next();
// vs.setMinimized(true);
// }
//
// // Restore the animation pref
// preferenceStore.setValue(IWorkbenchPreferenceConstants.ENABLE_ANIMATIONS, useAnimations);
//
// // this is a one-off deal...set during the extension reading
// minStacks.clear();
// layout = 0;
// }
// }
// else
// {
// // Update the FVB only if not using the new min/max
//
// // WorkbenchWindow wbw = (WorkbenchWindow) page.getWorkbenchWindow();
//// if (wbw != 0)
//// {
//// ITrimManager tbm = wbw.getTrimManager();
//// if (tbm != 0)
//// {
//// IWindowTrim fvb = tbm.getTrim(FastViewBar.FASTVIEWBAR_ID);
//// if (fvb instanceof FastViewBar)
//// {
//// ((FastViewBar)fvb).update(true);
//// }
//// }
//// }
// }
// // If we are -not- using the new min/max then ensure that there
// // are no stacks in the trim. This can happen when a user switches
// // back to the 3.0 presentation...
// if (!Perspective.useNewMinMax(this) && fastViewManager != 0)
// {
// bool stacksWereRestored = fastViewManager.restoreAllTrimStacks();
// setEditorAreaTrimVisibility(false);
//
// // Restore any 'maximized' view stack since we've restored
// // the minimized stacks
// if (stacksWereRestored && presentation.getMaximizedStack().Cast<ViewStack>() != 0)
// {
// ViewStack vs = (ViewStack) presentation.getMaximizedStack();
// vs.setPresentationState(IStackPresentationSite.STATE_RESTORED);
// presentation.setMaximizedStack(0);
// }
// }
// We hide the editor area -after- the presentation activates
if (hideEditorArea)
{
// We do this here to ensure that createPartControl is called on the
// top editor
// before it is hidden. See bug 20166.
this->HideEditorArea();
shouldHideEditorsOnActivate = false;
// // this is an override so it should handle both states
// if (useNewMinMax)
// setEditorAreaTrimVisibility(editorAreaState == IStackPresentationSite.STATE_MINIMIZED);
}
// Fix perspectives whose contributing bundle has gone away
FixOrphan();
}
void Perspective::FixOrphan()
{
PerspectiveRegistry* reg = static_cast<PerspectiveRegistry*>(PlatformUI::GetWorkbench()->GetPerspectiveRegistry());
IPerspectiveDescriptor::Pointer regDesc = reg->FindPerspectiveWithId(descriptor->GetId());
if (regDesc.IsNull())
{
QString msg = "Perspective " + descriptor->GetLabel() + " has been made into a local copy";
IStatus::Pointer status = StatusUtil::NewStatus(IStatus::WARNING_TYPE, msg, BERRY_STATUS_LOC);
//StatusManager.getManager().handle(status, StatusManager.LOG);
WorkbenchPlugin::Log(status);
QString localCopyLabel("<%1>");
QString newDescId = localCopyLabel.arg(descriptor->GetLabel());
while (reg->FindPerspectiveWithId(newDescId).IsNotNull())
{
newDescId = localCopyLabel.arg(newDescId);
}
IPerspectiveDescriptor::Pointer newDesc = reg->CreatePerspective(newDescId, descriptor);
page->SavePerspectiveAs(newDesc);
}
}
void Perspective::OnDeactivate()
{
presentation->Deactivate();
//setActiveFastView(0);
//setAllPinsVisible(false);
// // Update fast views.
// if (fastViewManager != 0)
// {
// List fastViews = fastViewManager.getFastViews(0);
// for (int i = 0; i < fastViews.size(); i++)
// {
// ViewPane pane = getPane((IViewReference) fastViews.get(i));
// if (pane != 0)
// {
// Control ctrl = pane.getControl();
// if (ctrl != 0)
// {
// ctrl.setEnabled(true); // Add focus support.
// }
// }
// }
//
// fastViewManager.deActivate();
// }
//
// // Ensure that the editor area trim is hidden as well
// setEditorAreaTrimVisibility(false);
}
void Perspective::PartActivated(IWorkbenchPart::Pointer /*activePart*/)
{
// // If a fastview is open close it.
// if (activeFastView != 0
// && activeFastView.getPart(false) != activePart)
// {
// setActiveFastView(0);
// }
}
void Perspective::PerformedShowIn(const QString& /*partId*/)
{
//showInTimes.insert(std::make_pair(partId, new Long(System.currentTimeMillis())));
}
bool Perspective::RestoreState(IMemento::Pointer memento)
{
// MultiStatus result = new MultiStatus(
// PlatformUI.PLUGIN_ID,
// IStatus.OK,
// WorkbenchMessages.Perspective_problemsRestoringPerspective, 0);
bool result = true;
// Create persp descriptor.
descriptor = new PerspectiveDescriptor("", "", PerspectiveDescriptor::Pointer(nullptr));
//result.add(descriptor.restoreState(memento));
result &= descriptor->RestoreState(memento);
PerspectiveDescriptor::Pointer desc = WorkbenchPlugin::GetDefault()->
GetPerspectiveRegistry()->FindPerspectiveWithId(descriptor->GetId()).Cast<PerspectiveDescriptor>();
if (desc)
{
descriptor = desc;
}
this->memento = memento;
// Add the visible views.
QList<IMemento::Pointer> views(memento->GetChildren(WorkbenchConstants::TAG_VIEW));
//result.merge(createReferences(views));
result &= this->CreateReferences(views);
memento = memento->GetChild(WorkbenchConstants::TAG_FAST_VIEWS);
if (memento)
{
views = memento->GetChildren(WorkbenchConstants::TAG_VIEW);
//result.merge(createReferences(views));
result &= this->CreateReferences(views);
}
return result;
}
bool Perspective::CreateReferences(const QList<IMemento::Pointer>& views)
{
// MultiStatus result = new MultiStatus(PlatformUI.PLUGIN_ID, IStatus.OK,
// WorkbenchMessages.Perspective_problemsRestoringViews, 0);
bool result = true;
for (int x = 0; x < views.size(); x++)
{
// Get the view details.
IMemento::Pointer childMem = views[x];
QString id; childMem->GetString(WorkbenchConstants::TAG_ID, id);
// skip creation of the intro reference - it's handled elsewhere.
if (id == IntroConstants::INTRO_VIEW_ID)
{
continue;
}
QString secondaryId(ViewFactory::ExtractSecondaryId(id));
if (!secondaryId.isEmpty())
{
id = ViewFactory::ExtractPrimaryId(id);
}
// Create and open the view.
try
{
QString rm; childMem->GetString(WorkbenchConstants::TAG_REMOVED, rm);
if (rm != "true")
{
viewFactory->CreateView(id, secondaryId);
}
}
catch (const PartInitException& e)
{
childMem->PutString(WorkbenchConstants::TAG_REMOVED, "true");
// result.add(StatusUtil.newStatus(IStatus.ERR,
// e.getMessage() == 0 ? "" : e.getMessage(), //$NON-NLS-1$
// e));
WorkbenchPlugin::Log(e.what(), e);
result &= true;
}
}
return result;
}
bool Perspective::RestoreState()
{
if (this->memento == 0)
{
//return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, 0, "", 0); //$NON-NLS-1$
return true;
}
// MultiStatus result = new MultiStatus(
// PlatformUI.PLUGIN_ID,
// IStatus.OK,
// WorkbenchMessages.Perspective_problemsRestoringPerspective, 0);
- bool result = true;
IMemento::Pointer memento = this->memento;
this->memento = nullptr;
const IMemento::Pointer boundsMem(memento->GetChild(WorkbenchConstants::TAG_WINDOW));
if (boundsMem)
{
int x, y, w, h;
boundsMem->GetInteger(WorkbenchConstants::TAG_X, x);
boundsMem->GetInteger(WorkbenchConstants::TAG_Y, y);
boundsMem->GetInteger(WorkbenchConstants::TAG_HEIGHT, h);
boundsMem->GetInteger(WorkbenchConstants::TAG_WIDTH, w);
QRect r(x, y, w, h);
//StartupThreading.runWithoutExceptions(new StartupRunnable()
// {
// void runWithException() throws Throwable
// {
if (page->GetWorkbenchWindow()->GetActivePage() == 0)
{
page->GetWorkbenchWindow()->GetShell()->SetBounds(r);
}
// }
// });
}
// Create an empty presentation..
PerspectiveHelper* pres;
//StartupThreading.runWithoutExceptions(new StartupRunnable()
// {
// void runWithException() throws Throwable
// {
ViewSashContainer::Pointer mainLayout(new ViewSashContainer(page, this->GetClientComposite()));
pres = new PerspectiveHelper(page, mainLayout, this);
// }});
// Read the layout.
// result.merge(pres.restoreState(memento
// .getChild(IWorkbenchConstants.TAG_LAYOUT)));
- result &= pres->RestoreState(memento->GetChild(WorkbenchConstants::TAG_LAYOUT));
-
+ pres->RestoreState(memento->GetChild(WorkbenchConstants::TAG_LAYOUT));
//StartupThreading.runWithoutExceptions(new StartupRunnable()
// {
// void runWithException() throws Throwable
// {
// Add the editor workbook. Do not hide it now.
pres->ReplacePlaceholderWithPart(editorArea);
// }});
// Add the visible views.
QList<IMemento::Pointer> views(memento->GetChildren(WorkbenchConstants::TAG_VIEW));
for (int x = 0; x < views.size(); x++)
{
// Get the view details.
IMemento::Pointer childMem = views[x];
QString id; childMem->GetString(WorkbenchConstants::TAG_ID, id);
QString secondaryId(ViewFactory::ExtractSecondaryId(id));
if (!secondaryId.isEmpty())
{
id = ViewFactory::ExtractPrimaryId(id);
}
// skip the intro as it is restored higher up in workbench.
if (id == IntroConstants::INTRO_VIEW_ID)
{
continue;
}
// Create and open the view.
IViewReference::Pointer viewRef = viewFactory->GetView(id, secondaryId);
WorkbenchPartReference::Pointer ref = viewRef.Cast<WorkbenchPartReference>();
// report error
if (ref == 0)
{
QString key = ViewFactory::GetKey(id, secondaryId);
// result.add(new Status(IStatus.ERR, PlatformUI.PLUGIN_ID, 0,
// NLS.bind(WorkbenchMessages.Perspective_couldNotFind, key ), 0));
WorkbenchPlugin::Log("Could not find view: " + key);
continue;
}
bool willPartBeVisible = pres->WillPartBeVisible(ref->GetId(), secondaryId);
if (willPartBeVisible)
{
IViewPart::Pointer view = ref->GetPart(true).Cast<IViewPart>();
if (view)
{
ViewSite::Pointer site = view->GetSite().Cast<ViewSite>();
pres->ReplacePlaceholderWithPart(site->GetPane().Cast<LayoutPart>());
}
}
else
{
pres->ReplacePlaceholderWithPart(ref->GetPane());
}
}
// // Load the fast views
// if (fastViewManager != 0)
// fastViewManager.restoreState(memento, result);
// Load the view layout recs
QList<IMemento::Pointer> recMementos(memento
->GetChildren(WorkbenchConstants::TAG_VIEW_LAYOUT_REC));
for (int i = 0; i < recMementos.size(); i++)
{
IMemento::Pointer recMemento = recMementos[i];
QString compoundId;
if (recMemento->GetString(WorkbenchConstants::TAG_ID, compoundId))
{
ViewLayoutRec::Pointer rec = GetViewLayoutRec(compoundId, true);
QString closeablestr; recMemento->GetString(WorkbenchConstants::TAG_CLOSEABLE, closeablestr);
if (WorkbenchConstants::FALSE_VAL == closeablestr)
{
rec->isCloseable = false;
}
QString moveablestr; recMemento->GetString(WorkbenchConstants::TAG_MOVEABLE, moveablestr);
if (WorkbenchConstants::FALSE_VAL == moveablestr)
{
rec->isMoveable = false;
}
QString standalonestr; recMemento->GetString(WorkbenchConstants::TAG_STANDALONE, standalonestr);
if (WorkbenchConstants::TRUE_VAL == standalonestr)
{
rec->isStandalone = true;
QString showstr; recMemento->GetString(WorkbenchConstants::TAG_SHOW_TITLE, showstr);
rec->showTitle = WorkbenchConstants::FALSE_VAL != showstr;
}
}
}
//final IContextService service = (IContextService)page.getWorkbenchWindow().getService(IContextService.class);
try
{ // one big try block, don't kill me here
// // defer context events
// if (service != 0)
// {
// service.activateContext(ContextAuthority.DEFER_EVENTS);
// }
//
// HashSet knownActionSetIds = new HashSet();
//
// // Load the always on action sets.
QList<IMemento::Pointer> actions; // = memento
// .getChildren(IWorkbenchConstants.TAG_ALWAYS_ON_ACTION_SET);
// for (int x = 0; x < actions.length; x++)
// {
// String actionSetID = actions[x]
// .getString(IWorkbenchConstants.TAG_ID);
// final IActionSetDescriptor d = WorkbenchPlugin.getDefault()
// .getActionSetRegistry().findActionSet(actionSetID);
// if (d != 0)
// {
// StartupThreading
// .runWithoutExceptions(new StartupRunnable()
// {
// void runWithException() throws Throwable
// {
// addAlwaysOn(d);
// }
// });
//
// knownActionSetIds.add(actionSetID);
// }
// }
//
// // Load the always off action sets.
// actions = memento
// .getChildren(IWorkbenchConstants.TAG_ALWAYS_OFF_ACTION_SET);
// for (int x = 0; x < actions.length; x++)
// {
// String actionSetID = actions[x]
// .getString(IWorkbenchConstants.TAG_ID);
// final IActionSetDescriptor d = WorkbenchPlugin.getDefault()
// .getActionSetRegistry().findActionSet(actionSetID);
// if (d != 0)
// {
// StartupThreading
// .runWithoutExceptions(new StartupRunnable()
// {
// void runWithException() throws Throwable
// {
// addAlwaysOff(d);
// }
// });
// knownActionSetIds.add(actionSetID);
// }
// }
// Load "show view actions".
actions = memento->GetChildren(WorkbenchConstants::TAG_SHOW_VIEW_ACTION);
for (int x = 0; x < actions.size(); x++)
{
QString id; actions[x]->GetString(WorkbenchConstants::TAG_ID, id);
showViewShortcuts.push_back(id);
}
// // Load "show in times".
// actions = memento.getChildren(IWorkbenchConstants.TAG_SHOW_IN_TIME);
// for (int x = 0; x < actions.length; x++)
// {
// String id = actions[x].getString(IWorkbenchConstants.TAG_ID);
// String timeStr = actions[x]
// .getString(IWorkbenchConstants.TAG_TIME);
// if (id != 0 && timeStr != 0)
// {
// try
// {
// long time = Long.parseLong(timeStr);
// showInTimes.put(id, new Long(time));
// }
// catch (NumberFormatException e)
// {
// // skip this one
// }
// }
// }
// Load "show in parts" from registry, not memento
showInPartIds = this->GetShowInIdsFromRegistry();
// // Load "new wizard actions".
// actions = memento
// .getChildren(IWorkbenchConstants.TAG_NEW_WIZARD_ACTION);
// newWizardShortcuts = new ArrayList(actions.length);
// for (int x = 0; x < actions.length; x++)
// {
// String id = actions[x].getString(IWorkbenchConstants.TAG_ID);
// newWizardShortcuts.add(id);
// }
// Load "perspective actions".
actions = memento->GetChildren(WorkbenchConstants::TAG_PERSPECTIVE_ACTION);
for (int x = 0; x < actions.size(); x++)
{
QString id; actions[x]->GetString(WorkbenchConstants::TAG_ID, id);
perspectiveShortcuts.push_back(id);
}
// ArrayList extActionSets = getPerspectiveExtensionActionSets();
// for (int i = 0; i < extActionSets.size(); i++)
// {
// String actionSetID = (String) extActionSets.get(i);
// if (knownActionSetIds.contains(actionSetID))
// {
// continue;
// }
// final IActionSetDescriptor d = WorkbenchPlugin.getDefault()
// .getActionSetRegistry().findActionSet(actionSetID);
// if (d != 0)
// {
// StartupThreading
// .runWithoutExceptions(new StartupRunnable()
// {
// void runWithException() throws Throwable
// {
// addAlwaysOn(d);
// }
// });
// knownActionSetIds.add(d.getId());
// }
// }
// // Add the visible set of action sets to our knownActionSetIds
// // Now go through the registry to ensure we pick up any new action
// // sets
// // that have been added but not yet considered by this perspective.
// ActionSetRegistry reg = WorkbenchPlugin.getDefault()
// .getActionSetRegistry();
// IActionSetDescriptor[] array = reg.getActionSets();
// int count = array.length;
// for (int i = 0; i < count; i++)
// {
// IActionSetDescriptor desc = array[i];
// if ((!knownActionSetIds.contains(desc.getId()))
// && (desc.isInitiallyVisible()))
// {
// addActionSet(desc);
// }
// }
}
catch (...)
{
// // restart context changes
// if (service != 0)
// {
// StartupThreading.runWithoutExceptions(new StartupRunnable()
// {
// void runWithException() throws Throwable
// {
// service.activateContext(ContextAuthority.SEND_EVENTS);
// }
// });
// }
}
// Save presentation.
presentation = pres;
// Hide the editor area if needed. Need to wait for the
// presentation to be fully setup first.
int areaVisible = 0;
bool areaVisibleExists = memento->GetInteger(WorkbenchConstants::TAG_AREA_VISIBLE, areaVisible);
// Rather than hiding the editors now we must wait until after their
// controls
// are created. This ensures that if an editor is instantiated,
// createPartControl
// is also called. See bug 20166.
shouldHideEditorsOnActivate = (areaVisibleExists && areaVisible == 0);
// // Restore the trim state of the editor area
// IPreferenceStore preferenceStore = PrefUtil.getAPIPreferenceStore();
// bool useNewMinMax = preferenceStore.getbool(IWorkbenchPreferenceConstants.ENABLE_NEW_MIN_MAX);
// if (useNewMinMax)
// {
// Integer trimStateInt = memento.getInteger(IWorkbenchConstants.TAG_AREA_TRIM_STATE);
// if (trimStateInt != 0)
// {
// editorAreaState = trimStateInt.intValue() & 0x3; // low order two bits contain the state
// editorAreaRestoreOnUnzoom = (trimStateInt.intValue() & 4) != 0;
// }
// }
// restore the fixed state
int isFixed = 0;
fixed = (memento->GetInteger(WorkbenchConstants::TAG_FIXED, isFixed) && isFixed == 1);
return true;
}
QList<QString> Perspective::GetShowInIdsFromRegistry()
{
PerspectiveExtensionReader reader;
QList<QString> tags;
tags.push_back(WorkbenchRegistryConstants::TAG_SHOW_IN_PART);
reader.SetIncludeOnlyTags(tags);
PageLayout::Pointer layout(new PageLayout());
reader.ExtendLayout(nullptr, descriptor->GetOriginalId(), layout);
return layout->GetShowInPartIds();
}
void Perspective::SaveDesc()
{
this->SaveDescAs(descriptor);
}
void Perspective::SaveDescAs(IPerspectiveDescriptor::Pointer desc)
{
PerspectiveDescriptor::Pointer realDesc = desc.Cast<PerspectiveDescriptor>();
//get the layout from the registry
PerspectiveRegistry* perspRegistry = dynamic_cast<PerspectiveRegistry*>(
WorkbenchPlugin::GetDefault()->GetPerspectiveRegistry());
// Capture the layout state.
XMLMemento::Pointer memento = XMLMemento::CreateWriteRoot("perspective");
//IStatus::Pointer status = SaveState(memento, realDesc, false);
// if (status->GetSeverity() == IStatus::ERROR_TYPE)
// {
// ErrorDialog.openError((Shell) 0, "Saving Problems",
// "Unable to store layout state.",
// status);
// return;
// }
bool status = SaveState(memento, realDesc, false);
if (!status)
{
QMessageBox::critical(nullptr, "Saving Problems", "Unable to store layout state.");
return;
}
//save it to the preference store
try
{
perspRegistry->SaveCustomPersp(realDesc, memento.GetPointer());
descriptor = realDesc;
}
catch (const std::exception& /*e*/)
{
perspRegistry->DeletePerspective(realDesc);
QMessageBox::critical(nullptr, "Saving Problems", "Unable to store layout state.");
}
}
bool Perspective::SaveState(IMemento::Pointer memento)
{
// MultiStatus::Pointer result(new MultiStatus(
// PlatformUI::PLUGIN_ID(),
// IStatus::OK_TYPE,
// "Problems occurred saving perspective.",
// BERRY_STATUS_LOC));
// result->Merge(SaveState(memento, descriptor, true));
bool result = true;
result &= this->SaveState(memento, descriptor, true);
return result;
}
bool Perspective::SaveState(IMemento::Pointer memento, PerspectiveDescriptor::Pointer p,
bool saveInnerViewState)
{
// MultiStatus::Pointer result(new MultiStatus(
// PlatformUI::PLUGIN_ID(),
// IStatus::OK_TYPE,
// "Problems occurred saving perspective.",
// BERRY_STATUS_LOC));
bool result = true;
if (this->memento)
{
memento->PutMemento(this->memento);
return result;
}
// Save the version number.
memento->PutString(WorkbenchConstants::TAG_VERSION, VERSION_STRING);
//result.add(p.saveState(memento));
result &= p->SaveState(memento);
if (!saveInnerViewState)
{
QRect bounds(page->GetWorkbenchWindow()->GetShell()->GetBounds());
IMemento::Pointer boundsMem = memento
->CreateChild(WorkbenchConstants::TAG_WINDOW);
boundsMem->PutInteger(WorkbenchConstants::TAG_X, bounds.x());
boundsMem->PutInteger(WorkbenchConstants::TAG_Y, bounds.y());
boundsMem->PutInteger(WorkbenchConstants::TAG_HEIGHT, bounds.height());
boundsMem->PutInteger(WorkbenchConstants::TAG_WIDTH, bounds.width());
}
// // Save the "always on" action sets.
// Iterator itr = alwaysOnActionSets.iterator();
// while (itr.hasNext())
// {
// IActionSetDescriptor desc = (IActionSetDescriptor) itr.next();
// IMemento child = memento
// .createChild(IWorkbenchConstants.TAG_ALWAYS_ON_ACTION_SET);
// child.putString(IWorkbenchConstants.TAG_ID, desc.getId());
// }
// // Save the "always off" action sets.
// itr = alwaysOffActionSets.iterator();
// while (itr.hasNext())
// {
// IActionSetDescriptor desc = (IActionSetDescriptor) itr.next();
// IMemento child = memento
// .createChild(IWorkbenchConstants.TAG_ALWAYS_OFF_ACTION_SET);
// child.putString(IWorkbenchConstants.TAG_ID, desc.getId());
// }
// Save "show view actions"
for (QList<QString>::iterator itr = showViewShortcuts.begin();
itr != showViewShortcuts.end(); ++itr)
{
IMemento::Pointer child = memento
->CreateChild(WorkbenchConstants::TAG_SHOW_VIEW_ACTION);
child->PutString(WorkbenchConstants::TAG_ID, *itr);
}
// // Save "show in times"
// itr = showInTimes.keySet().iterator();
// while (itr.hasNext())
// {
// String id = (String) itr.next();
// Long time = (Long) showInTimes.get(id);
// IMemento child = memento
// .createChild(IWorkbenchConstants.TAG_SHOW_IN_TIME);
// child.putString(IWorkbenchConstants.TAG_ID, id);
// child.putString(IWorkbenchConstants.TAG_TIME, time.toString());
// }
// // Save "new wizard actions".
// itr = newWizardShortcuts.iterator();
// while (itr.hasNext())
// {
// String str = (String) itr.next();
// IMemento child = memento
// .createChild(IWorkbenchConstants.TAG_NEW_WIZARD_ACTION);
// child.putString(IWorkbenchConstants.TAG_ID, str);
// }
// Save "perspective actions".
for (QList<QString>::iterator itr = perspectiveShortcuts.begin();
itr != perspectiveShortcuts.end(); ++itr)
{
IMemento::Pointer child = memento
->CreateChild(WorkbenchConstants::TAG_PERSPECTIVE_ACTION);
child->PutString(WorkbenchConstants::TAG_ID, *itr);
}
// Get visible views.
QList<PartPane::Pointer> viewPanes;
presentation->CollectViewPanes(viewPanes);
// Save the views.
for (QList<PartPane::Pointer>::iterator itr = viewPanes.begin();
itr != viewPanes.end(); ++itr)
{
IWorkbenchPartReference::Pointer ref((*itr)->GetPartReference());
IViewDescriptor::Pointer desc = page->GetViewFactory()->GetViewRegistry()
->Find(ref->GetId());
if(desc && desc->IsRestorable())
{
IMemento::Pointer viewMemento = memento
->CreateChild(WorkbenchConstants::TAG_VIEW);
viewMemento->PutString(WorkbenchConstants::TAG_ID, ViewFactory::GetKey(ref.Cast<IViewReference>()));
}
}
// // save all fastview state
// if (fastViewManager != 0)
// fastViewManager.saveState(memento);
// Save the view layout recs.
for (QHash<QString, ViewLayoutRec::Pointer>::iterator i = mapIDtoViewLayoutRec.begin();
i != mapIDtoViewLayoutRec.end(); ++i)
{
QString compoundId(i.key());
ViewLayoutRec::Pointer rec(i.value());
if (rec && (!rec->isCloseable || !rec->isMoveable || rec->isStandalone))
{
IMemento::Pointer layoutMemento(memento
->CreateChild(WorkbenchConstants::TAG_VIEW_LAYOUT_REC));
layoutMemento->PutString(WorkbenchConstants::TAG_ID, compoundId);
if (!rec->isCloseable)
{
layoutMemento->PutString(WorkbenchConstants::TAG_CLOSEABLE,
WorkbenchConstants::FALSE_VAL);
}
if (!rec->isMoveable)
{
layoutMemento->PutString(WorkbenchConstants::TAG_MOVEABLE,
WorkbenchConstants::FALSE_VAL);
}
if (rec->isStandalone)
{
layoutMemento->PutString(WorkbenchConstants::TAG_STANDALONE,
WorkbenchConstants::TRUE_VAL);
layoutMemento->PutString(WorkbenchConstants::TAG_SHOW_TITLE,
rec->showTitle ? WorkbenchConstants::TRUE_VAL : WorkbenchConstants::FALSE_VAL);
}
}
}
// Save the layout.
IMemento::Pointer childMem(memento->CreateChild(WorkbenchConstants::TAG_LAYOUT));
//result->Add(presentation->SaveState(childMem));
result &= presentation->SaveState(childMem);
// Save the editor visibility state
if (this->IsEditorAreaVisible())
{
memento->PutInteger(WorkbenchConstants::TAG_AREA_VISIBLE, 1);
}
else
{
memento->PutInteger(WorkbenchConstants::TAG_AREA_VISIBLE, 0);
}
// // Save the trim state of the editor area if using the new min/max
// IPreferenceStore preferenceStore = PrefUtil.getAPIPreferenceStore();
// bool useNewMinMax = preferenceStore.getbool(IWorkbenchPreferenceConstants.ENABLE_NEW_MIN_MAX);
// if (useNewMinMax)
// {
// int trimState = editorAreaState;
// trimState |= editorAreaRestoreOnUnzoom ? 4 : 0;
// memento.putInteger(IWorkbenchConstants.TAG_AREA_TRIM_STATE, trimState);
// }
// Save the fixed state
if (fixed)
{
memento->PutInteger(WorkbenchConstants::TAG_FIXED, 1);
}
else
{
memento->PutInteger(WorkbenchConstants::TAG_FIXED, 0);
}
return result;
}
void Perspective::SetPerspectiveActionIds(const QList<QString>& list)
{
perspectiveShortcuts = list;
}
void Perspective::SetShowInPartIds(const QList<QString>& list)
{
showInPartIds = list;
}
void Perspective::SetShowViewActionIds(const QList<QString>& list)
{
showViewShortcuts = list;
}
void Perspective::ShowEditorArea()
{
if (this->IsEditorAreaVisible())
{
return;
}
editorHidden = false;
// Show the editor in the appropriate location
if (this->UseNewMinMax(Perspective::Pointer(this)))
{
bool isMinimized = editorAreaState == IStackPresentationSite::STATE_MINIMIZED;
if (!isMinimized)
{
// If the editor area is going to show then we have to restore
// if (getPresentation().getMaximizedStack() != 0)
// getPresentation().getMaximizedStack().setState(IStackPresentationSite.STATE_RESTORED);
this->ShowEditorAreaLocal();
}
// else
// setEditorAreaTrimVisibility(true);
}
else
{
this->ShowEditorAreaLocal();
}
}
void Perspective::ShowEditorAreaLocal()
{
if (editorHolder == 0 || editorHidden)
{
return;
}
// Replace the part holder with the editor area.
presentation->GetLayout()->Replace(editorHolder, editorArea);
editorHolder = nullptr;
}
void Perspective::SetEditorAreaState(int newState)
{
if (newState == editorAreaState)
return;
editorAreaState = newState;
// // reset the restore flag if we're not minimized
// if (newState != IStackPresentationSite::STATE_MINIMIZED)
// editorAreaRestoreOnUnzoom = false;
this->RefreshEditorAreaVisibility();
}
int Perspective::GetEditorAreaState()
{
return editorAreaState;
}
void Perspective::RefreshEditorAreaVisibility()
{
// Nothing shows up if the editor area isn't visible at all
if (editorHidden)
{
this->HideEditorAreaLocal();
//setEditorAreaTrimVisibility(false);
return;
}
PartStack::Pointer editorStack = editorArea.Cast<EditorSashContainer>()->GetUpperRightEditorStack();
if (editorStack == 0)
return;
// Whatever we're doing, make the current editor stack match it
//editorStack->SetStateLocal(editorAreaState);
// If it's minimized then it's in the trim
if (editorAreaState == IStackPresentationSite::STATE_MINIMIZED)
{
// Hide the editor area and show its trim
this->HideEditorAreaLocal();
//setEditorAreaTrimVisibility(true);
}
else
{
// Show the editor area and hide its trim
//setEditorAreaTrimVisibility(false);
this->ShowEditorAreaLocal();
// if (editorAreaState == IStackPresentationSite::STATE_MAXIMIZED)
// getPresentation().setMaximizedStack(editorStack);
}
}
IViewReference::Pointer Perspective::GetViewReference(const QString& viewId, const QString& secondaryId)
{
IViewReference::Pointer ref = page->FindViewReference(viewId, secondaryId);
if (ref == 0)
{
ViewFactory* factory = this->GetViewFactory();
try
{
ref = factory->CreateView(viewId, secondaryId);
}
catch (PartInitException& /*e*/)
{
// IStatus status = StatusUtil.newStatus(IStatus.ERR,
// e.getMessage() == 0 ? "" : e.getMessage(), //$NON-NLS-1$
// e);
// StatusUtil.handleStatus(status, "Failed to create view: id=" + viewId, //$NON-NLS-1$
// StatusManager.LOG);
//TODO Perspective status message
WorkbenchPlugin::Log("Failed to create view: id=" + viewId);
}
}
return ref;
}
IViewPart::Pointer Perspective::ShowView(const QString& viewId, const QString& secondaryId)
{
ViewFactory* factory = this->GetViewFactory();
IViewReference::Pointer ref = factory->CreateView(viewId, secondaryId);
IViewPart::Pointer part = ref->GetPart(true).Cast<IViewPart>();
if (part == 0)
{
throw PartInitException("Could not create view: " + ref->GetId());
}
PartSite::Pointer site = part->GetSite().Cast<PartSite>();
PartPane::Pointer pane = site->GetPane();
//TODO Perspective preference store
// IPreferenceStore store = WorkbenchPlugin.getDefault()
// .getPreferenceStore();
// int openViewMode = store.getInt(IPreferenceConstants.OPEN_VIEW_MODE);
//
// if (openViewMode == IPreferenceConstants.OVM_FAST &&
// fastViewManager != 0)
// {
// fastViewManager.addViewReference(FastViewBar.FASTVIEWBAR_ID, -1, ref, true);
// setActiveFastView(ref);
// }
// else if (openViewMode == IPreferenceConstants.OVM_FLOAT
// && presentation.canDetach())
// {
// presentation.addDetachedPart(pane);
// }
// else
// {
if (this->UseNewMinMax(Perspective::Pointer(this)))
{
// Is this view going to show in the trim?
// LayoutPart vPart = presentation.findPart(viewId, secondaryId);
// Determine if there is a trim stack that should get the view
QString trimId;
// // If we can locate the correct trim stack then do so
// if (vPart != 0)
// {
// String id = 0;
// ILayoutContainer container = vPart.getContainer();
// if (container.Cast<ContainerPlaceholder>() != 0)
// id = ((ContainerPlaceholder)container).getID();
// else if (container.Cast<ViewStack>() != 0)
// id = ((ViewStack)container).getID();
//
// // Is this place-holder in the trim?
// if (id != 0 && fastViewManager.getFastViews(id).size()> 0)
// {
// trimId = id;
// }
// }
//
// // No explicit trim found; If we're maximized then we either have to find an
// // arbitrary stack...
// if (trimId == 0 && presentation.getMaximizedStack() != 0)
// {
// if (vPart == 0)
// {
// ViewStackTrimToolBar blTrimStack = fastViewManager.getBottomRightTrimStack();
// if (blTrimStack != 0)
// {
// // OK, we've found a trim stack to add it to...
// trimId = blTrimStack.getId();
//
// // Since there was no placeholder we have to add one
// LayoutPart blPart = presentation.findPart(trimId, 0);
// if (blPart.Cast<ContainerPlaceholder>() != 0)
// {
// ContainerPlaceholder cph = (ContainerPlaceholder) blPart;
// if (cph.getRealContainer().Cast<ViewStack>() != 0)
// {
// ViewStack vs = (ViewStack) cph.getRealContainer();
//
// // Create a 'compound' id if this is a multi-instance part
// String compoundId = ref.getId();
// if (ref.getSecondaryId() != 0)
// compoundId = compoundId + ':' + ref.getSecondaryId();
//
// // Add the new placeholder
// vs.add(new PartPlaceholder(compoundId));
// }
// }
// }
// }
// }
//
// // If we have a trim stack located then add the view to it
// if (trimId != "")
// {
// fastViewManager.addViewReference(trimId, -1, ref, true);
// }
// else
// {
// bool inMaximizedStack = vPart != 0 && vPart.getContainer() == presentation.getMaximizedStack();
// Do the default behavior
presentation->AddPart(pane);
// // Now, if we're maximized then we have to minimize the new stack
// if (presentation.getMaximizedStack() != 0 && !inMaximizedStack)
// {
// vPart = presentation.findPart(viewId, secondaryId);
// if (vPart != 0 && vPart.getContainer().Cast<ViewStack>() != 0)
// {
// ViewStack vs = (ViewStack)vPart.getContainer();
// vs.setState(IStackPresentationSite.STATE_MINIMIZED);
//
// // setting the state to minimized will create the trim toolbar
// // so we don't need a 0 pointer check here...
// fastViewManager.getViewStackTrimToolbar(vs.getID()).setRestoreOnUnzoom(true);
// }
// }
// }
}
else
{
presentation->AddPart(pane);
}
//}
// Ensure that the newly showing part is enabled
if (pane != 0 && pane->GetControl() != nullptr)
Tweaklets::Get(GuiWidgetsTweaklet::KEY)->SetEnabled(pane->GetControl(), true);
return part;
}
IWorkbenchPartReference::Pointer Perspective::GetOldPartRef()
{
return oldPartRef;
}
void Perspective::SetOldPartRef(IWorkbenchPartReference::Pointer oldPartRef)
{
this->oldPartRef = oldPartRef;
}
bool Perspective::IsCloseable(IViewReference::Pointer reference)
{
ViewLayoutRec::Pointer rec = this->GetViewLayoutRec(reference, false);
if (rec != 0)
{
return rec->isCloseable;
}
return true;
}
bool Perspective::IsMoveable(IViewReference::Pointer reference)
{
ViewLayoutRec::Pointer rec = this->GetViewLayoutRec(reference, false);
if (rec != 0)
{
return rec->isMoveable;
}
return true;
}
void Perspective::DescribeLayout(QString& buf) const
{
// QList<IViewReference::Pointer> fastViews = getFastViews();
//
// if (fastViews.length != 0)
// {
// buf.append("fastviews ("); //$NON-NLS-1$
// for (int idx = 0; idx < fastViews.length; idx++)
// {
// IViewReference ref = fastViews[idx];
//
// if (idx> 0)
// {
// buf.append(", "); //$NON-NLS-1$
// }
//
// buf.append(ref.getPartName());
// }
// buf.append("), "); //$NON-NLS-1$
// }
this->GetPresentation()->DescribeLayout(buf);
}
void Perspective::TestInvariants()
{
this->GetPresentation()->GetLayout()->TestInvariants();
}
bool Perspective::UseNewMinMax(Perspective::Pointer activePerspective)
{
// We need to have an active perspective
if (activePerspective == 0)
return false;
// We need to have a trim manager (if we don't then we
// don't create a FastViewManager because it'd be useless)
// if (activePerspective->GetFastViewManager() == 0)
// return false;
// Make sure we don't NPE anyplace
WorkbenchWindow::Pointer wbw = activePerspective->page->GetWorkbenchWindow().Cast<WorkbenchWindow>();
if (wbw == 0)
return false;
// WorkbenchWindowConfigurer* configurer = wbw->GetWindowConfigurer();
// if (configurer == 0)
// return false;
IPresentationFactory* factory = WorkbenchPlugin::GetDefault()->GetPresentationFactory();
if (factory == nullptr)
return false;
// Ok, we should be good to go, return the pref
//IPreferenceStore preferenceStore = PrefUtil.getAPIPreferenceStore();
//bool useNewMinMax = preferenceStore.getbool(IWorkbenchPreferenceConstants.ENABLE_NEW_MIN_MAX);
return true;
}
}
diff --git a/Plugins/org.blueberry.ui.qt/src/internal/berryPerspectiveHelper.h b/Plugins/org.blueberry.ui.qt/src/internal/berryPerspectiveHelper.h
index d5d7d52b3c..c1c9cf1bcd 100755
--- a/Plugins/org.blueberry.ui.qt/src/internal/berryPerspectiveHelper.h
+++ b/Plugins/org.blueberry.ui.qt/src/internal/berryPerspectiveHelper.h
@@ -1,552 +1,552 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef BERRYPERSPECTIVEHELPER_H_
#define BERRYPERSPECTIVEHELPER_H_
#include "berryWorkbenchPage.h"
#include "berryPartPlaceholder.h"
#include "berryPerspective.h"
#include "berryViewSashContainer.h"
#include "berryPartPlaceholder.h"
#include "berryDetachedWindow.h"
#include "berryDetachedPlaceHolder.h"
#include "berryIDragOverListener.h"
#include "berryAbstractDropTarget.h"
#include "berryPartPane.h"
namespace berry
{
class WorkbenchPage;
/**
* A perspective presentation is a collection of parts with a layout. Each part
* is parented to a main window, so you can create more than one presentation
* on a set of parts and change the layout just by activating / deactivating a
* presentation.
*
* In addition, the user can change the position of any part by mouse
* manipulation (drag & drop). If a part is removed, we leave a placeholder
* behind to indicate where it goes should the part be added back.
*/
class PerspectiveHelper
{
friend class PartStack;
friend class ViewSashContainer;
private:
QScopedPointer<ctkException> tmpStackTrace;
QString tmpViewId;
WorkbenchPage* page;
protected:
Perspective* perspective;
QWidget* parentWidget;
private:
ViewSashContainer::Pointer mainLayout;
//private: PartStack maximizedStack;
/**
* If there is a ViewStack maximized on shutdown the id is
* cached and restored into this field on 'restoreState'.
* This is then used to bash the ViewStack's presentation state
* into the correct value on activation (the startup life-cycle
* is such that we have to use this 'latch' because the window
* state isn't valid until the activate happens.
*/
//private: String maximizedStackId;
private:
typedef QList<DetachedWindow::Pointer> DetachedWindowsType;
DetachedWindowsType detachedWindowList;
typedef QList<DetachedPlaceHolder::Pointer> DetachedPlaceHoldersType;
DetachedPlaceHoldersType detachedPlaceHolderList;
/**
* Maps a stack's id to its current bounds
* this is used to capture the current bounds of all
* stacks -before- starting a maximize (since the
* iterative 'minimize' calls cause the intial stack's
* bounds to change.
*/
QHash<QString, QRect> boundsMap;
bool detachable;
protected:
bool active;
// key is the LayoutPart object, value is the PartDragDrop object
//private: IPartDropListener partDropListener;
private:
static const int MIN_DETACH_WIDTH;
static const int MIN_DETACH_HEIGHT;
struct DragOverListener: public IDragOverListener
{
DragOverListener(PerspectiveHelper* perspHelper);
IDropTarget::Pointer Drag(QWidget* currentControl,
const Object::Pointer& draggedObject, const QPoint& position,
const QRect& dragRectangle) override;
private:
PerspectiveHelper* perspHelper;
};
QScopedPointer<IDragOverListener> dragTarget;
struct ActualDropTarget: public AbstractDropTarget
{
berryObjectMacro(ActualDropTarget);
/**
* @param part
* @param dragRectangle
* @since 3.1
*/
void SetTarget(PartPane::Pointer part, const QRect& dragRectangle);
/**
* @param part
* @param dragRectangle
* @since 3.1
*/
void SetTarget(PartStack::Pointer part, const QRect& dragRectangle);
ActualDropTarget(PerspectiveHelper* perspHelper, PartPane::Pointer part, const QRect& dragRectangle);
ActualDropTarget(PerspectiveHelper* perspHelper, PartStack::Pointer part, const QRect& dragRectangle);
void Drop() override;
CursorType GetCursor() override;
private:
PartPane::Pointer part;
PartStack::Pointer stack;
QRect dragRectangle;
PerspectiveHelper* perspHelper;
};
ActualDropTarget::Pointer dropTarget;
private:
struct MatchingPart
{
QString pid;
QString sid;
LayoutPart::Pointer part;
bool hasWildcard;
int len;
MatchingPart(const QString& pid, const QString& sid,
LayoutPart::Pointer part);
};
- struct CompareMatchingParts: public std::binary_function<MatchingPart, MatchingPart, bool>
+ struct CompareMatchingParts
{
bool operator()(const MatchingPart& m1, const MatchingPart& m2) const;
};
public:
/**
* Constructs a new object.
*/
PerspectiveHelper(WorkbenchPage* workbenchPage,
ViewSashContainer::Pointer mainLayout, Perspective* perspective);
/**
* Show the presentation.
*/
void Activate(QWidget* parent);
/**
* Adds a part to the presentation. If a placeholder exists for the part
* then swap the part in. Otherwise, add the part in the bottom right
* corner of the presentation.
*/
void AddPart(LayoutPart::Pointer part);
/**
* Attaches a part that was previously detached to the mainLayout.
*
* @param ref
*/
void AttachPart(IViewReference::Pointer ref);
/**
* Return whether detachable parts can be supported.
*/
bool CanDetach();
/**
* Bring a part forward so it is visible.
*
* @return true if the part was brought to top, false if not.
*/
bool BringPartToTop(LayoutPart::Pointer part);
/**
* Returns true if the given part is visible.
* A part is visible if it's top-level (not in a tab folder) or if it is the top one
* in a tab folder.
*/
bool IsPartVisible(IWorkbenchPartReference::Pointer partRef);
/**
* Returns true is not in a tab folder or if it is the top one in a tab
* folder.
*/
bool WillPartBeVisible(const QString& partId);
bool WillPartBeVisible(const QString& partId,
const QString& secondaryId);
/**
* Answer a list of the view panes.
*/
void CollectViewPanes(QList<PartPane::Pointer>& result);
/**
* Hide the presentation.
*/
void Deactivate();
~PerspectiveHelper();
/**
* Writes a description of the layout to the given string buffer.
* This is used for drag-drop test suites to determine if two layouts are the
* same. Like a hash code, the description should compare as equal iff the
* layouts are the same. However, it should be user-readable in order to
* help debug failed tests. Although these are english readable strings,
* they should not be translated or equality tests will fail.
* <p>
* This is only intended for use by test suites.
* </p>
*
* @param buf
*/
void DescribeLayout(QString& buf) const;
private:
/**
* Answer a list of the PartPlaceholder objects.
*/
QList<PartPlaceholder::Pointer> CollectPlaceholders();
/**
* Answer a list of the PartPlaceholder objects.
*/
QList<PartPlaceholder::Pointer> CollectPlaceholders(
const QList<LayoutPart::Pointer>& parts);
/**
* Answer a list of the view panes.
*/
void CollectViewPanes(QList<PartPane::Pointer>& result,
const QList<LayoutPart::Pointer>& parts);
/**
* Create a detached window containing a part.
*/
void DetachPart(LayoutPart::Pointer source, int x, int y);
void Detach(LayoutPart::Pointer source, int x, int y);
protected:
/**
* Deref a given part. Deconstruct its container as required. Do not remove
* drag listeners.
*/
void DerefPart(LayoutPart::Pointer part);
/**
* Detached a part from the mainLayout. Presently this does not use placeholders
* since the current implementation is not robust enough to remember a view's position
* in more than one root container. For now the view is simply derefed and will dock
* in the default position when attachPart is called.
*
* By default parts detached this way are set to float on top of the workbench
* without docking. It is assumed that people that want to drag a part back onto
* the WorkbenchWindow will detach it via drag and drop.
*
* @param ref
*/
public:
void DetachPart(IViewReference::Pointer ref);
/**
* Create a detached window containing a part.
*/
public:
void AddDetachedPart(LayoutPart::Pointer part);
public:
void AddDetachedPart(LayoutPart::Pointer part, const QRect& bounds);
/**
* disableDragging.
*/
private:
void DisableAllDrag();
/**
* enableDragging.
*/
private:
void EnableAllDrag();
/**
* Find the first part with a given ID in the presentation.
* Wild cards now supported.
*/
private:
LayoutPart::Pointer FindPart(const QString& id);
/**
* Find the first part that matches the specified
* primary and secondary id pair. Wild cards
* are supported.
*/
public:
LayoutPart::Pointer FindPart(const QString& primaryId,
const QString& secondaryId);
/**
* Find the first part with a given ID in the presentation.
*/
private:
LayoutPart::Pointer FindPart(const QString& id,
const QList<LayoutPart::Pointer>& parts,
QList<MatchingPart>& matchingParts);
/**
* Find the first part that matches the specified
* primary and secondary id pair. Wild cards
* are supported.
*/
private:
LayoutPart::Pointer FindPart(const QString& primaryId,
const QString& secondaryId,
const QList<LayoutPart::Pointer>& parts,
QList<MatchingPart>& matchingParts);
/**
* Returns true if a placeholder exists for a given ID.
*/
public:
bool HasPlaceholder(const QString& id);
/**
* Returns true if a placeholder exists for a given ID.
* @since 3.0
*/
public:
bool HasPlaceholder(const QString& primaryId,
const QString& secondaryId);
/**
* Returns the layout container.
*/
public:
PartSashContainer::Pointer GetLayout() const;
/**
* Gets the active state.
*/
public:
bool IsActive();
/**
* Returns whether the presentation is zoomed.
*
* <strong>NOTE:</strong> As of 3.3 this method should always return 'false'
* when using the new min/max behavior. It is only used for
* legacy 'zoom' handling.
*/
// public: bool IsZoomed() {
// return mainLayout.getZoomedPart() != null;
// }
/**
* @return The currently maxmized stack (if any)
*/
// public: PartStack::Pointer GetMaximizedStack() {
// return maximizedStack;
// }
/**
* Sets the currently maximized stack. Used for query
* and 'unZoom' purposes in the 3.3 presentation.
*
* @param stack The newly maximized stack
*/
// public: void SetMaximizedStack(PartStack::Pointer stack) {
// if (stack == maximizedStack)
// return;
//
// maximizedStack = stack;
// }
/**
* Returns the ratio that should be used when docking the given source
* part onto the given target
*
* @param source newly added part
* @param target existing part being dragged over
* @return the final size of the source part (wrt the current size of target)
* after it is docked
*/
public:
static float GetDockingRatio(LayoutPart::Pointer source,
LayoutPart::Pointer target);
/**
* Returns whether changes to a part will affect zoom. There are a few
* conditions for this .. - we are zoomed. - the part is contained in the
* main window. - the part is not the zoom part - the part is not a fast
* view - the part and the zoom part are not in the same editor workbook
* - the part and the zoom part are not in the same view stack.
*/
// public: bool PartChangeAffectsZoom(LayoutPart::Pointer pane) {
// return pane.isObscuredByZoom();
// }
/**
* Remove all references to a part.
*/
public:
void RemovePart(LayoutPart::Pointer part);
/**
* Add a part to the presentation.
*
* Note: unlike all other LayoutParts, PartPlaceholders will still point to
* their parent container even when it is inactive. This method relies on this
* fact to locate the parent.
*/
public:
void ReplacePlaceholderWithPart(LayoutPart::Pointer part);
/**
* @see org.blueberry.ui.IPersistable
*/
public:
bool RestoreState(IMemento::Pointer memento);
/**
* @see org.blueberry.ui.IPersistable
*/
public:
bool SaveState(IMemento::Pointer memento);
/**
* Zoom in on a particular layout part.
*/
// public: void zoomIn(IWorkbenchPartReference ref) {
// PartPane pane = ((WorkbenchPartReference) ref).getPane();
//
//
// parentWidget.setRedraw(false);
// try {
// pane.requestZoomIn();
// } finally {
// parentWidget.setRedraw(true);
// }
// }
/**
* Zoom out.
*/
// public: void zoomOut() {
// // New 3.3 behavior
// if (Perspective.useNewMinMax(perspective)) {
// if (maximizedStack != null)
// maximizedStack.setState(IStackPresentationSite.STATE_RESTORED);
// return;
// }
//
// LayoutPart zoomPart = mainLayout.getZoomedPart();
// if (zoomPart != null) {
// zoomPart.requestZoomOut();
// }
// }
/**
* Forces the perspective to have no zoomed or minimized parts.
* This is used when switching to the 3.3 presentation...
*/
// public: void forceNoZoom() {
// // Ensure that nobody's zoomed
// zoomOut();
//
// // Now, walk the layout ensuring that nothing is minimized
// LayoutPart[] kids = mainLayout.getChildren();
// for (int i = 0; i < kids.length; i++) {
// if (kids[i] instanceof ViewStack) {
// ((ViewStack)kids[i]).setMinimized(false);
// }
// else if (kids[i] instanceof EditorSashContainer) {
// LayoutPart[] editorStacks = ((EditorSashContainer)kids[i]).getChildren();
// for (int j = 0; j < editorStacks.length; j++) {
// if (editorStacks[j] instanceof EditorStack) {
// ((EditorStack)editorStacks[j]).setMinimized(false);
// }
// }
// }
// }
// }
/**
* Captures the current bounds of all ViewStacks and the editor
* area and puts them into an ID -> QRect map. This info is
* used to cache the bounds so that we can correctly place minimized
* stacks during a 'maximized' operation (where the iterative min's
* affect the current layout while being performed.
*/
public:
void UpdateBoundsMap();
/**
* Resets the bounds map so that it won't interfere with normal minimize
* operations
*/
public:
void ResetBoundsMap();
public:
QRect GetCachedBoundsFor(const QString& id);
};
}
#endif /* BERRYPERSPECTIVEHELPER_H_ */
diff --git a/Plugins/org.blueberry.ui.qt/src/internal/berryWorkbenchPage.h b/Plugins/org.blueberry.ui.qt/src/internal/berryWorkbenchPage.h
index f2abe33ad0..fe4c59e7ac 100644
--- a/Plugins/org.blueberry.ui.qt/src/internal/berryWorkbenchPage.h
+++ b/Plugins/org.blueberry.ui.qt/src/internal/berryWorkbenchPage.h
@@ -1,1429 +1,1428 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef BERRYWORKBENCHPAGE_H_
#define BERRYWORKBENCHPAGE_H_
#include <berryIAdaptable.h>
#include "berryIWorkbenchPage.h"
#include "berryIWorkbenchPartReference.h"
#include "berryIReusableEditor.h"
#include "berryILayoutContainer.h"
#include "berryIStickyViewManager.h"
#include "berryWorkbenchPagePartList.h"
#include "berryWorkbenchPartReference.h"
#include "berryPageSelectionService.h"
#include "berryEditorManager.h"
#include "berryViewFactory.h"
#include "berryPartPane.h"
#include <list>
namespace berry
{
struct IExtensionPoint;
struct IExtensionTracker;
//class PartPane;
//class PartPane::Sashes;
class EditorAreaHelper;
class WorkbenchWindow;
class Perspective;
class PerspectiveHelper;
class PerspectiveDescriptor;
class LayoutPartSash;
class LayoutTree;
class LayoutTreeNode;
class PartService;
/**
* \ingroup org_blueberry_ui_internal
*
* A collection of views and editors in a workbench.
*/
class BERRY_UI_QT WorkbenchPage: public IWorkbenchPage
{
public:
berryObjectMacro(WorkbenchPage);
protected:
//TODO Weakpointer
WorkbenchWindow* window;
friend class ViewFactory;
friend class WorkbenchWindow;
friend class EditorAreaHelper;
friend class WWinPartService;
private:
/**
* Manages editor contributions and action set part associations.
*/
class ActionSwitcher
{
public:
ActionSwitcher(WorkbenchPage* page);
/**
* Updates the contributions given the new part as the active part.
*
* @param newPart
* the new active part, may be <code>null</code>
*/
void UpdateActivePart(IWorkbenchPart::Pointer newPart);
/**
* Updates the contributions given the new part as the topEditor.
*
* @param newEditor
* the new top editor, may be <code>null</code>
*/
void UpdateTopEditor(IEditorPart::Pointer newEditor);
private:
/**
* Activates the contributions of the given part. If <code>enable</code>
* is <code>true</code> the contributions are visible and enabled,
* otherwise they are disabled.
*
* @param part
* the part whose contributions are to be activated
* @param enable
* <code>true</code> the contributions are to be enabled,
* not just visible.
*/
void ActivateContributions(IWorkbenchPart::Pointer part, bool enable);
/**
* Deactivates the contributions of the given part. If <code>remove</code>
* is <code>true</code> the contributions are removed, otherwise they
* are disabled.
*
* @param part
* the part whose contributions are to be deactivated
* @param remove
* <code>true</code> the contributions are to be removed,
* not just disabled.
*/
void DeactivateContributions(IWorkbenchPart::Pointer part, bool remove);
WorkbenchPage* page;
IWorkbenchPart::WeakPtr activePart;
IEditorPart::WeakPtr topEditor;
};
class ActivationList
{
public:
//List of parts in the activation order (oldest first)
typedef std::deque<IWorkbenchPartReference::Pointer> PartListType;
typedef std::deque<IWorkbenchPartReference::Pointer>::iterator PartListIter;
typedef std::deque<IWorkbenchPartReference::Pointer>::reverse_iterator PartListReverseIter;
private:
PartListType parts;
WorkbenchPage* page;
public:
ActivationList(WorkbenchPage* page);
/*
* Add/Move the active part to end of the list;
*/
void SetActive(SmartPointer<IWorkbenchPart> part);
/*
* Ensures that the given part appears AFTER any other part in the same
* container.
*/
void BringToTop(SmartPointer<IWorkbenchPartReference> ref);
/*
* Returns the last (most recent) iterator (index) of the given container in the activation list, or returns
* end() if the given container does not appear in the activation list.
*/
PartListIter LastIndexOfContainer(SmartPointer<ILayoutContainer> container);
/*
* Add/Move the active part to end of the list;
*/
void SetActive(SmartPointer<IWorkbenchPartReference> ref);
/*
* Add the active part to the beginning of the list.
*/
void Add(SmartPointer<IWorkbenchPartReference> ref);
/*
* Return the active part. Filter fast views.
*/
SmartPointer<IWorkbenchPart> GetActive();
/*
* Return the previously active part. Filter fast views.
*/
SmartPointer<IWorkbenchPart> GetPreviouslyActive();
SmartPointer<IWorkbenchPartReference> GetActiveReference(bool editorsOnly);
/*
* Retuns the index of the part within the activation list. The higher
* the index, the more recently it was used.
*/
PartListIter IndexOf(SmartPointer<IWorkbenchPart> part);
/*
* Returns the index of the part reference within the activation list.
* The higher the index, the more recent it was used.
*/
PartListIter IndexOf(SmartPointer<IWorkbenchPartReference> ref);
/*
* Remove a part from the list
*/
bool Remove(SmartPointer<IWorkbenchPartReference> ref);
/*
* Returns the topmost editor on the stack, or null if none.
*/
SmartPointer<IEditorPart> GetTopEditor();
/*
* Returns the editors in activation order (oldest first).
*/
QList<SmartPointer<IEditorReference> > GetEditors();
/*
* Return a list with all parts (editors and views).
*/
QList<SmartPointer<IWorkbenchPartReference> > GetParts();
private:
SmartPointer<IWorkbenchPart> GetActive(PartListIter start);
SmartPointer<IWorkbenchPartReference> GetActiveReference(PartListIter start, bool editorsOnly);
/*
* Find a part in the list starting from the end and filter
* and views from other perspectives. Will filter fast views
* unless 'includeActiveFastViews' is true;
*/
SmartPointer<IWorkbenchPartReference> GetActiveReference(PartListIter start, bool editorsOnly, bool skipPartsObscuredByZoom);
};
/**
* Helper class to keep track of all opened perspective. Both the opened
* and used order is kept.
*/
struct PerspectiveList
{
public:
typedef QList<SmartPointer<Perspective> > PerspectiveListType;
typedef PerspectiveListType::iterator iterator;
private:
/**
* List of perspectives in the order they were opened;
*/
PerspectiveListType openedList;
/**
* List of perspectives in the order they were used. Last element is
* the most recently used, and first element is the least recently
* used.
*/
PerspectiveListType usedList;
/**
* The perspective explicitly set as being the active one
*/
SmartPointer<Perspective> active;
void UpdateActionSets(SmartPointer<Perspective> oldPersp,
SmartPointer<Perspective> newPersp);
public:
/**
* Creates an empty instance of the perspective list
*/
PerspectiveList();
/**
* Update the order of the perspectives in the opened list
*
* @param perspective
* @param newLoc
*/
void Reorder(IPerspectiveDescriptor::Pointer perspective, int newLoc);
/**
* Return all perspectives in the order they were activated.
*
* @return an array of perspectives sorted by activation order, least
* recently activated perspective last.
*/
PerspectiveListType GetSortedPerspectives();
/**
* Adds a perspective to the list. No check is done for a duplicate when
* adding.
* @param perspective the perspective to add
* @return boolean <code>true</code> if the perspective was added
*/
bool Add(SmartPointer<Perspective> perspective);
/**
* Returns an iterator on the perspective list in the order they were
* opened.
*/
PerspectiveListType::iterator Begin();
PerspectiveListType::iterator End();
/**
* Returns an array with all opened perspectives
*/
PerspectiveListType GetOpenedPerspectives();
/**
* Removes a perspective from the list.
*/
bool Remove(SmartPointer<Perspective> perspective);
/**
* Swap the opened order of old perspective with the new perspective.
*/
void Swap(SmartPointer<Perspective> oldPerspective,
SmartPointer<Perspective> newPerspective);
/**
* Returns whether the list contains any perspectives
*/
bool IsEmpty();
/**
* Returns the most recently used perspective in the list.
*/
SmartPointer<Perspective> GetActive();
/**
* Returns the next most recently used perspective in the list.
*/
SmartPointer<Perspective> GetNextActive();
/**
* Returns the number of perspectives opened
*/
PerspectiveListType::size_type Size();
/**
* Marks the specified perspective as the most recently used one in the
* list.
*/
void SetActive(SmartPointer<Perspective> perspective);
};
IAdaptable* input;
QWidget* composite;
//Could be delete. This information is in the active part list;
ActivationList* activationList;
EditorManager* editorMgr;
EditorAreaHelper* editorPresentation;
//ListenerList propertyChangeListeners = new ListenerList();
PageSelectionService* selectionService;
QScopedPointer<WorkbenchPagePartList> partList; // = new WorkbenchPagePartList(selectionService);
//IActionBars actionBars;
ViewFactory* viewFactory;
PerspectiveList perspList;
SmartPointer<PerspectiveDescriptor> deferredActivePersp;
//NavigationHistory navigationHistory = new NavigationHistory(this);
IStickyViewManager::Pointer stickyViewMan;
/**
* Returns true if perspective with given id contains view with given id
*/
bool HasView(const QString& perspectiveId, const QString& viewId);
/**
* If we're in the process of activating a part, this points to the new part.
* Otherwise, this is null.
*/
IWorkbenchPartReference::Pointer partBeingActivated;
/**
* Contains a list of perspectives that may be dirty due to plugin
* installation and removal.
*/
std::set<QString> dirtyPerspectives;
ActionSwitcher actionSwitcher;
mutable QScopedPointer<IExtensionTracker> tracker;
// Deferral count... delays disposing parts and sending certain events if nonzero
int deferCount;
// Parts waiting to be disposed
QList<WorkbenchPartReference::Pointer> pendingDisposals;
SmartPointer<IExtensionPoint> GetPerspectiveExtensionPoint();
public:
/**
* Constructs a new page with a given perspective and input.
*
* @param w
* the parent window
* @param layoutID
* must not be <code>null</code>
* @param input
* the page input
* @throws WorkbenchException
* on null layout id
*/
WorkbenchPage(WorkbenchWindow* w, const QString& layoutID,
IAdaptable* input);
/**
* Constructs a page. <code>restoreState(IMemento)</code> should be
* called to restore this page from data stored in a persistance file.
*
* @param w
* the parent window
* @param input
* the page input
* @throws WorkbenchException
*/
WorkbenchPage(WorkbenchWindow* w, IAdaptable* input);
~WorkbenchPage() override;
/**
* Activates a part. The part will be brought to the front and given focus.
*
* @param part
* the part to activate
*/
void Activate(IWorkbenchPart::Pointer part) override;
/**
* Adds an IPartListener to the part service.
*/
void AddPartListener(IPartListener* l) override;
/*
* (non-Javadoc) Method declared on ISelectionListener.
*/
void AddSelectionListener(ISelectionListener* listener) override;
/*
* (non-Javadoc) Method declared on ISelectionListener.
*/
void AddSelectionListener(const QString& partId,
ISelectionListener* listener) override;
/*
* (non-Javadoc) Method declared on ISelectionListener.
*/
void AddPostSelectionListener(ISelectionListener* listener) override;
/*
* (non-Javadoc) Method declared on ISelectionListener.
*/
void AddPostSelectionListener(const QString& partId,
ISelectionListener* listener) override;
/**
* Moves a part forward in the Z order of a perspective so it is visible.
* If the part is in the same stack as the active part, the new part is
* activated.
*
* @param part
* the part to bring to move forward
*/
void BringToTop(IWorkbenchPart::Pointer part) override;
private:
/**
* Activates a part. The part is given focus, the pane is hilighted.
*/
void ActivatePart(const IWorkbenchPart::Pointer part);
ILayoutContainer::Pointer GetContainer(IWorkbenchPart::Pointer part);
ILayoutContainer::Pointer GetContainer(IWorkbenchPartReference::Pointer part);
SmartPointer<PartPane> GetPane(IWorkbenchPart::Pointer part);
SmartPointer<PartPane> GetPane(IWorkbenchPartReference::Pointer part);
/**
* Brings a part to the front of its stack. Does not update the active part or
* active editor. This should only be called if the caller knows that the part
* is not in the same stack as the active part or active editor, or if the caller
* is prepared to update activation after the call.
*
* @param part
*/
bool InternalBringToTop(IWorkbenchPartReference::Pointer part);
/**
* Resets the layout for the perspective. The active part in the old layout
* is activated in the new layout for consistent user context.
*
* Assumes the busy cursor is active.
*/
void BusyResetPerspective();
/**
* Implements <code>setPerspective</code>.
*
* Assumes that busy cursor is active.
*
* @param desc
* identifies the new perspective.
*/
void BusySetPerspective(IPerspectiveDescriptor::Pointer desc);
/*
* Performs showing of the view in the given mode.
*/
void BusyShowView(IViewPart::Pointer part, int mode);
/**
* Returns whether a part exists in the current page.
*/
bool CertifyPart(IWorkbenchPart::Pointer part);
protected:
/**
* Shows a view.
*
* Assumes that a busy cursor is active.
*/
IViewPart::Pointer BusyShowView(const QString& viewID,
const QString& secondaryID, int mode);
public:
void UpdateActionBars();
/**
* Removes the perspective which match the given description.
*
* @param desc
* identifies the perspective to be removed.
*/
void RemovePerspective(IPerspectiveDescriptor::Pointer desc);
/**
* Closes the perspective.
*/
bool Close() override;
/**
* See IWorkbenchPage
*/
bool CloseAllSavedEditors();
/**
* See IWorkbenchPage
*/
bool CloseAllEditors(bool save) override;
/**
* See IWorkbenchPage
*/
bool CloseEditors(const QList<IEditorReference::Pointer>& refArray,
bool save) override;
private:
void UpdateActivePart();
/**
* Makes the given part active. Brings it in front if necessary. Permits null
* (indicating that no part should be active).
*
* @param ref new active part (or null)
*/
void MakeActive(IWorkbenchPartReference::Pointer ref);
/**
* Makes the given editor active. Brings it to front if necessary. Permits <code>null</code>
* (indicating that no editor is active).
*
* @param ref the editor to make active, or <code>null</code> for no active editor
*/
void MakeActiveEditor(IEditorReference::Pointer ref);
/**
* Enables or disables listener notifications. This is used to delay listener notifications until the
* end of a public method.
*
* @param shouldDefer
*/
void DeferUpdates(bool shouldDefer);
void StartDeferring();
void HandleDeferredEvents();
bool IsDeferred();
public:
/**
* See IWorkbenchPage#closeEditor
*/
bool CloseEditor(IEditorReference::Pointer editorRef, bool save);
/**
* See IWorkbenchPage#closeEditor
*/
bool CloseEditor(IEditorPart::Pointer editor, bool save) override;
/**
* Closes current perspective. If last perspective, then entire page
* is closed.
*
* @param saveParts
* whether the page's parts should be saved if closed
* @param closePage
* whether the page itself should be closed if last perspective
*/
void CloseCurrentPerspective(bool saveParts, bool closePage);
/**
* @see IWorkbenchPage#closePerspective(IPerspectiveDescriptor, boolean, boolean)
*/
void ClosePerspective(IPerspectiveDescriptor::Pointer desc, bool saveParts,
bool closePage) override;
/**
* @see IWorkbenchPage#closeAllPerspectives(boolean, boolean)
*/
void CloseAllPerspectives(bool saveEditors, bool closePage) override;
protected:
/**
* Closes the specified perspective. If last perspective, then entire page
* is closed.
*
* @param persp
* the perspective to be closed
* @param saveParts
* whether the parts that are being closed should be saved
* (editors if last perspective, views if not shown in other
* parspectives)
*/
void ClosePerspective(SmartPointer<Perspective> persp, bool saveParts,
bool closePage);
/**
* This is called by child objects after a part has been added to the page.
* The page will in turn notify its listeners.
*/
void PartAdded(WorkbenchPartReference::Pointer ref);
/**
* This is called by child objects after a part has been added to the page.
* The part will be queued for disposal after all listeners have been notified
*/
void PartRemoved(WorkbenchPartReference::Pointer ref);
private:
/**
* Creates the client composite.
*/
void CreateClientComposite();
/**
* Creates a new view set. Return null on failure.
*
* @param desc the perspective descriptor
* @param notify whether to fire a perspective opened event
*/
SmartPointer<Perspective> CreatePerspective(SmartPointer<PerspectiveDescriptor> desc,
bool notify);
void DisposePart(WorkbenchPartReference::Pointer ref);
/**
* Deactivates a part. The pane is unhilighted.
*/
void DeactivatePart(IWorkbenchPart::Pointer part);
/**
* Dispose a perspective.
*
* @param persp the perspective descriptor
* @param notify whether to fire a perspective closed event
*/
void DisposePerspective(SmartPointer<Perspective> persp, bool notify);
public:
/**
* Detaches a view from the WorkbenchWindow.
*/
void DetachView(IViewReference::Pointer ref);
/**
* Removes a detachedwindow.
*/
void AttachView(IViewReference::Pointer ref);
/**
* Returns the first view manager with given ID.
*/
SmartPointer<Perspective> FindPerspective(IPerspectiveDescriptor::Pointer desc);
/**
* See IWorkbenchPage@findView.
*/
IViewPart::Pointer FindView(const QString& id) override;
/*
* (non-Javadoc)
*
* @see org.blueberry.ui.IWorkbenchPage
*/
IViewReference::Pointer FindViewReference(const QString& viewId) override;
/*
* (non-Javadoc)
*
* @see org.blueberry.ui.IWorkbenchPage
*/
IViewReference::Pointer FindViewReference(const QString& viewId,
const QString& secondaryId) override;
/**
* Notify property change listeners about a property change.
*
* @param changeId
* the change id
* @param oldValue
* old property value
* @param newValue
* new property value
*/
//private: void FirePropertyChange(String changeId, Object oldValue,
// Object newValue) {
//
// UIListenerLogging.logPagePropertyChanged(this, changeId, oldValue, newValue);
//
// Object[] listeners = propertyChangeListeners.getListeners();
// PropertyChangeEvent event = new PropertyChangeEvent(this, changeId,
// oldValue, newValue);
//
// for (int i = 0; i < listeners.length; i++) {
// ((IPropertyChangeListener) listeners[i]).propertyChange(event);
// }
// }
/**
* @see IWorkbenchPage
*/
IEditorPart::Pointer GetActiveEditor() override;
/**
* Returns the reference for the active editor, or <code>null</code>
* if there is no active editor.
*
* @return the active editor reference or <code>null</code>
*/
IEditorReference::Pointer GetActiveEditorReference();
/*
* (non-Javadoc) Method declared on IPartService
*/
IWorkbenchPart::Pointer GetActivePart() override;
/*
* (non-Javadoc) Method declared on IPartService
*/
IWorkbenchPartReference::Pointer GetActivePartReference() override;
/**
* Returns the active perspective for the page, <code>null</code> if
* none.
*/
SmartPointer<Perspective> GetActivePerspective();
/**
* Returns the client composite.
*/
QWidget* GetClientComposite();
// for dynamic UI - change access from private to protected
// for testing purposes only, changed from protected to public
/**
* Answer the editor manager for this window.
*/
EditorManager* GetEditorManager();
/**
* Answer the perspective presentation.
*/
PerspectiveHelper* GetPerspectivePresentation();
/**
* Answer the editor presentation.
*/
EditorAreaHelper* GetEditorPresentation();
/**
* Allow access to the part service for this page ... used internally to
* propogate certain types of events to the page part listeners.
* @return the part service for this page.
*/
PartService* GetPartService();
/**
* See IWorkbenchPage.
*/
QList<IEditorPart::Pointer> GetEditors() override;
QList<IEditorPart::Pointer> GetDirtyEditors() override;
QList<ISaveablePart::Pointer> GetDirtyParts();
/**
* See IWorkbenchPage.
*/
IEditorPart::Pointer FindEditor(IEditorInput::Pointer input) override;
/**
* See IWorkbenchPage.
*/
QList<IEditorReference::Pointer> FindEditors(
IEditorInput::Pointer input, const QString& editorId, int matchFlags) override;
/**
* See IWorkbenchPage.
*/
QList<IEditorReference::Pointer> GetEditorReferences() override;
/**
* @see IWorkbenchPage
*/
IAdaptable* GetInput() override;
/**
* Returns the page label. This is a combination of the page input and
* active perspective.
*/
QString GetLabel() override;
/**
* Returns the perspective.
*/
IPerspectiveDescriptor::Pointer GetPerspective() override;
/*
* (non-Javadoc) Method declared on ISelectionService
*/
ISelection::ConstPointer GetSelection() const override;
/*
* (non-Javadoc) Method declared on ISelectionService
*/
ISelection::ConstPointer GetSelection(const QString& partId) override;
//public:
// SelectionEvents& GetSelectionEvents(const QString& partId = "");
/*
* Returns the view factory.
*/
ViewFactory* GetViewFactory();
/**
* See IWorkbenchPage.
*/
QList<IViewReference::Pointer> GetViewReferences() override;
/**
* See IWorkbenchPage.
*/
QList<IViewPart::Pointer> GetViews() override;
protected:
/**
* Returns all view parts in the specified perspective
*
* @param persp the perspective
* @return an array of view parts
*/
/*package*/
QList<IViewPart::Pointer> GetViews(SmartPointer<Perspective> persp,
bool restore);
/* package */
void RefreshActiveView();
public:
/**
* See IWorkbenchPage.
*/
IWorkbenchWindow::Pointer GetWorkbenchWindow() const override;
/*
* (non-Javadoc)
*
* @see org.blueberry.ui.IWorkbenchPage#hideView(org.blueberry.ui.IViewReference)
*/
void HideView(IViewReference::Pointer ref) override;
/**
* See IPerspective
*/
void HideView(IViewPart::Pointer view) override;
private:
/**
* Initialize the page.
*
* @param w
* the parent window
* @param layoutID
* may be <code>null</code> if restoring from file
* @param input
* the page input
* @param openExtras
* whether to process the perspective extras preference
*/
void Init(WorkbenchWindow* w, const QString& layoutID,
IAdaptable* input, bool openExtras);
/**
* Opens the perspectives specified in the PERSPECTIVE_BAR_EXTRAS preference (see bug 84226).
*/
public:
void OpenPerspectiveExtras();
/**
* See IWorkbenchPage.
*/
bool IsPartVisible(IWorkbenchPart::Pointer part) override;
/**
* See IWorkbenchPage.
*/
bool IsEditorAreaVisible();
/**
* Returns whether the view is fast.
*/
bool IsFastView(IViewReference::Pointer ref);
/**
* Return whether the view is closeable or not.
*
* @param ref the view reference to check. Must not be <code>null</code>.
* @return true if the part is closeable.
*/
bool IsCloseable(IViewReference::Pointer ref);
/**
* Return whether the view is moveable or not.
*
* @param ref the view reference to check. Must not be <code>null</code>.
* @return true if the part is moveable.
*/
bool IsMoveable(IViewReference::Pointer ref);
/**
* Returns whether the layout of the active
* perspective is fixed.
*/
bool IsFixedLayout();
protected:
/**
* Return true if the perspective has a dirty editor.
*/
bool IsSaveNeeded();
/**
* This method is called when the page is activated.
*/
void OnActivate();
/**
* This method is called when the page is deactivated.
*/
void OnDeactivate();
public:
/**
* See IWorkbenchPage.
*/
void ReuseEditor(IReusableEditor::Pointer editor, IEditorInput::Pointer input) override;
/**
* See IWorkbenchPage.
*/
IEditorPart::Pointer OpenEditor(IEditorInput::Pointer input,
const QString& editorID) override;
/**
* See IWorkbenchPage.
*/
IEditorPart::Pointer OpenEditor(IEditorInput::Pointer input,
const QString& editorID, bool activate) override;
/**
* See IWorkbenchPage.
*/
IEditorPart::Pointer OpenEditor(IEditorInput::Pointer input,
const QString& editorID, bool activate, int matchFlags) override;
/**
* This is not public API but for use internally. editorState can be <code>null</code>.
*/
IEditorPart::Pointer OpenEditor(IEditorInput::Pointer input,
const QString& editorID, bool activate, int matchFlags,
IMemento::Pointer editorState);
/*
* Added to fix Bug 178235 [EditorMgmt] DBCS 3.3 - Cannot open file with external program.
* Opens a new editor using the given input and descriptor. (Normally, editors are opened using
* an editor ID and an input.)
*/
IEditorPart::Pointer OpenEditorFromDescriptor(IEditorInput::Pointer input,
IEditorDescriptor::Pointer editorDescriptor, bool activate,
IMemento::Pointer editorState);
private:
/**
* @see #openEditor(IEditorInput, String, boolean, int)
*/
IEditorPart::Pointer BusyOpenEditor(IEditorInput::Pointer input,
const QString& editorID, bool activate, int matchFlags,
IMemento::Pointer editorState);
/*
* Added to fix Bug 178235 [EditorMgmt] DBCS 3.3 - Cannot open file with external program.
* See openEditorFromDescriptor().
*/
IEditorPart::Pointer BusyOpenEditorFromDescriptor(
IEditorInput::Pointer input,
EditorDescriptor::Pointer editorDescriptor, bool activate,
IMemento::Pointer editorState);
/*
* Added to fix Bug 178235 [EditorMgmt] DBCS 3.3 - Cannot open file with external program.
* See openEditorFromDescriptor().
*/
IEditorPart::Pointer BusyOpenEditorFromDescriptorBatched(
IEditorInput::Pointer input, EditorDescriptor::Pointer editorDescriptor,
bool activate, IMemento::Pointer editorState);
public:
void OpenEmptyTab();
/**
* See IWorkbenchPage.
*/
bool IsEditorPinned(IEditorPart::Pointer editor) override;
/**
* Removes an IPartListener from the part service.
*/
void RemovePartListener(IPartListener* l) override;
/*
* (non-Javadoc) Method declared on ISelectionListener.
*/
void RemoveSelectionListener(ISelectionListener* listener) override;
/*
* (non-Javadoc) Method declared on ISelectionListener.
*/
void RemoveSelectionListener(const QString& partId,
ISelectionListener* listener) override;
/*
* (non-Javadoc) Method declared on ISelectionListener.
*/
void RemovePostSelectionListener(ISelectionListener* listener) override;
/*
* (non-Javadoc) Method declared on ISelectionListener.
*/
void RemovePostSelectionListener(const QString& partId,
ISelectionListener* listener) override;
/**
* This method is called when a part is activated by clicking within it. In
* response, the part, the pane, and all of its actions will be activated.
*
* In the current design this method is invoked by the part pane when the
* pane, the part, or any children gain focus.
*/
void RequestActivation(IWorkbenchPart::Pointer part);
/**
* Resets the layout for the perspective. The active part in the old layout
* is activated in the new layout for consistent user context.
*/
void ResetPerspective() override;
/**
* Restore this page from the memento and ensure that the active
* perspective is equals the active descriptor otherwise create a new
* perspective for that descriptor. If activeDescriptor is null active the
* old perspective.
*/
/*IStatus*/bool RestoreState(IMemento::Pointer memento,
IPerspectiveDescriptor::Pointer activeDescriptor);
/**
* See IWorkbenchPage
*/
bool SaveAllEditors(bool confirm) override;
/**
* @param confirm
* @param addNonPartSources true if saveables from non-part sources should be saved too
* @return false if the user cancelled
*
*/
bool SaveAllEditors(bool confirm, bool addNonPartSources);
/**
* Saves an editors in the workbench. If <code>confirm</code> is <code>true</code>
* the user is prompted to confirm the command.
*
* @param confirm
* if user confirmation should be sought
* @return <code>true</code> if the command succeeded, or <code>false</code>
* if the user cancels the command
*/
bool SaveEditor(IEditorPart::Pointer editor, bool confirm) override;
/**
* Saves the current perspective.
*/
void SavePerspective() override;
/**
* Saves the perspective.
*/
void SavePerspectiveAs(IPerspectiveDescriptor::Pointer newDesc) override;
/**
* Save the state of the page.
*/
/*IStatus*/bool SaveState(IMemento::Pointer memento);
/**
* See IWorkbenchPage.
*/
void SetEditorAreaVisible(bool showEditorArea);
/**
* Sets the perspective.
*
* @param desc
* identifies the new perspective.
*/
void SetPerspective(IPerspectiveDescriptor::Pointer desc) override;
/**
* See IWorkbenchPage.
*/
IViewPart::Pointer ShowView(const QString& viewID) override;
/*
* (non-Javadoc)
*
* @see org.blueberry.ui.IWorkbenchPage#showView(java.lang.String,
* java.lang.String, int)
*/
IViewPart::Pointer ShowView(const QString& viewID,
const QString& secondaryID, int mode) override;
/*
* Returns the editors in activation order (oldest first).
*/
QList<IEditorReference::Pointer> GetSortedEditors();
/**
* @see IWorkbenchPage#getOpenPerspectives()
*/
QList<IPerspectiveDescriptor::Pointer> GetOpenPerspectives() override;
protected:
/**
* Do not call this method. Use <code>busyOpenEditor</code>.
*
* @see IWorkbenchPage#openEditor(IEditorInput, String, boolean)
*/
IEditorPart::Pointer BusyOpenEditorBatched(IEditorInput::Pointer input,
const QString& editorID, bool activate, int matchFlags,
IMemento::Pointer editorState);
void ShowEditor(bool activate, IEditorPart::Pointer editor);
/*
* Saves the workbench part.
*/
bool SavePart(ISaveablePart::Pointer saveable, IWorkbenchPart::Pointer part,
bool confirm);
/**
* Restore the toolbar layout for the active perspective.
*/
void ResetToolBarLayout();
/**
* Return all open Perspective objects.
*
* @return all open Perspective objects
*/
/*package*/
QList<SmartPointer<Perspective> > GetOpenInternalPerspectives();
/**
* Checks perspectives in the order they were activiated
* for the specfied part. The first sorted perspective
* that contains the specified part is returned.
*
* @param part specified part to search for
* @return the first sorted perspespective containing the part
*/
/*package*/
SmartPointer<Perspective> GetFirstPerspectiveWithView(IViewPart::Pointer part);
// for dynamic UI
void AddPerspective(SmartPointer<Perspective> persp);
public:
/**
* Returns the perspectives in activation order (oldest first).
*/
QList<IPerspectiveDescriptor::Pointer> GetSortedPerspectives() override;
/*
* Returns the parts in activation order (oldest first).
*/
QList<IWorkbenchPartReference::Pointer> GetSortedParts();
/**
* Returns the reference to the given part, or <code>null</code> if it has no reference
* (i.e. it is not a top-level part in this workbench page).
*
* @param part the part
* @return the part's reference or <code>null</code> if the given part does not belong
* to this workbench page
*/
IWorkbenchPartReference::Pointer GetReference(IWorkbenchPart::Pointer part) override;
/*
* (non-Javadoc)
*
* @see org.blueberry.ui.IWorkbenchPage#getViewStack(org.blueberry.ui.IViewPart)
*/
QList<IViewPart::Pointer> GetViewStack(IViewPart::Pointer part);
/**
* Allow for programmatically resizing a part.
* <p>
* <em>EXPERIMENTAL</em>
* </p>
* <p>
* Known limitations:
* <ul>
* <li>currently applies only to views</li>
* <li>has no effect when view is zoomed</li>
* </ul>
*/
void ResizeView(IViewPart::Pointer part, int width, int height);
/**
* Sanity-checks the objects in this page. Throws an Assertation exception
* if an object's internal state is invalid. ONLY INTENDED FOR USE IN THE
* UI TEST SUITES.
*/
void TestInvariants();
IExtensionTracker* GetExtensionTracker() const override;
/*
* (non-Javadoc)
*
* @see org.blueberry.ui.IWorkbenchPage#getPerspectiveShortcuts()
*/
QList<QString> GetPerspectiveShortcuts() override;
/*
* (non-Javadoc)
*
* @see org.blueberry.ui.IWorkbenchPage#getShowViewShortcuts()
*/
QList<QString> GetShowViewShortcuts() override;
bool IsPartVisible(IWorkbenchPartReference::Pointer reference);
private:
QString GetId(IWorkbenchPart::Pointer part);
QString GetId(IWorkbenchPartReference::Pointer ref);
/**
* Sets the active part.
*/
void SetActivePart(IWorkbenchPart::Pointer newPart);
/**
* Sets the layout of the page. Assumes the new perspective is not null.
* Keeps the active part if possible. Updates the window menubar and
* toolbar if necessary.
*/
void SetPerspective(SmartPointer<Perspective> newPersp);
/*
* Update visibility state of all views.
*/
void UpdateVisibility(SmartPointer<Perspective> oldPersp,
SmartPointer<Perspective> newPersp);
/**
* @param mode the mode to test
* @return whether the mode is recognized
*/
bool CertifyMode(int mode);
/**
* Find the stack of view references stacked with this view part.
*
* @param part
* the part
* @return the stack of references
*/
QList<IViewReference::Pointer> GetViewReferenceStack(
IViewPart::Pointer part);
- struct ActivationOrderPred : std::binary_function<IViewReference::Pointer,
- IViewReference::Pointer, bool>
+ struct ActivationOrderPred
{
ActivationOrderPred(ActivationList* partList);
ActivationList* activationList;
bool operator()(const IViewReference::Pointer o1, const IViewReference::Pointer o2) const;
};
// provides sash information for the given pane
struct SashInfo
{
SmartPointer<LayoutPartSash> right;
SmartPointer<LayoutPartSash> left;
SmartPointer<LayoutPartSash> top;
SmartPointer<LayoutPartSash> bottom;
SmartPointer<LayoutTreeNode> rightNode;
SmartPointer<LayoutTreeNode> leftNode;
SmartPointer<LayoutTreeNode> topNode;
SmartPointer<LayoutTreeNode> bottomNode;
};
void FindSashParts(SmartPointer<LayoutTree> tree, const PartPane::Sashes& sashes,
SashInfo& info);
protected:
/**
* Returns all parts that are owned by this page
*
* @return
*/
QList<IWorkbenchPartReference::Pointer> GetAllParts();
/**
* Returns all open parts that are owned by this page (that is, all parts
* for which a part opened event would have been sent -- these would be
* activated parts whose controls have already been created.
*/
QList<IWorkbenchPartReference::Pointer> GetOpenParts();
private:
void SuggestReset();
};
}
#endif /*BERRYWORKBENCHPAGE_H_*/
diff --git a/Plugins/org.mitk.gui.qt.basicimageprocessing/CMakeLists.txt b/Plugins/org.mitk.gui.qt.basicimageprocessing/CMakeLists.txt
index a6f5a3fb89..a7de496eac 100644
--- a/Plugins/org.mitk.gui.qt.basicimageprocessing/CMakeLists.txt
+++ b/Plugins/org.mitk.gui.qt.basicimageprocessing/CMakeLists.txt
@@ -1,7 +1,8 @@
project(org_mitk_gui_qt_basicimageprocessing)
mitk_create_plugin(
EXPORT_DIRECTIVE BASICIMAGEPROCESSING_EXPORT
EXPORTED_INCLUDE_SUFFIXES src
MODULE_DEPENDS MitkQtWidgetsExt MitkMapperExt MitkImageDenoising
+ PACKAGE_DEPENDS ITK|MathematicalMorphology+Smoothing
)
diff --git a/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp b/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp
index 98ccf8a397..ea07102a60 100644
--- a/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp
+++ b/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp
@@ -1,1432 +1,1431 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "QmitkExtWorkbenchWindowAdvisor.h"
#include "QmitkExtActionBarAdvisor.h"
#include <QMenu>
#include <QMenuBar>
#include <QMainWindow>
#include <QStatusBar>
#include <QString>
#include <QFile>
#include <QRegExp>
#include <QTextStream>
#include <QSettings>
#include <ctkPluginException.h>
#include <service/event/ctkEventAdmin.h>
#include <berryPlatform.h>
#include <berryPlatformUI.h>
#include <berryIActionBarConfigurer.h>
#include <berryIWorkbenchWindow.h>
#include <berryIWorkbenchPage.h>
#include <berryIPreferencesService.h>
#include <berryIPerspectiveRegistry.h>
#include <berryIPerspectiveDescriptor.h>
#include <berryIProduct.h>
#include <berryIWorkbenchPartConstants.h>
#include <berryQtPreferences.h>
#include <berryQtStyleManager.h>
#include <berryWorkbenchPlugin.h>
#include <internal/berryQtShowViewAction.h>
#include <internal/berryQtOpenPerspectiveAction.h>
#include <QmitkFileOpenAction.h>
#include <QmitkFileSaveAction.h>
#include <QmitkExtFileSaveProjectAction.h>
#include <QmitkFileExitAction.h>
#include <QmitkCloseProjectAction.h>
#include <QmitkUndoAction.h>
#include <QmitkRedoAction.h>
#include <QmitkDefaultDropTargetListener.h>
#include <QmitkStatusBar.h>
#include <QmitkProgressBar.h>
#include <QmitkMemoryUsageIndicatorView.h>
#include <QmitkPreferencesDialog.h>
#include <QmitkOpenDicomEditorAction.h>
#include <QmitkOpenMxNMultiWidgetEditorAction.h>
#include <QmitkOpenStdMultiWidgetEditorAction.h>
#include <itkConfigure.h>
-#include <vtkConfigure.h>
#include <mitkVersion.h>
#include <mitkIDataStorageService.h>
#include <mitkIDataStorageReference.h>
#include <mitkDataStorageEditorInput.h>
#include <mitkWorkbenchUtil.h>
#include <vtkVersionMacros.h>
// UGLYYY
#include "internal/QmitkExtWorkbenchWindowAdvisorHack.h"
#include "internal/QmitkCommonExtPlugin.h"
#include "mitkUndoController.h"
#include "mitkVerboseLimitedLinearUndo.h"
#include <QToolBar>
#include <QToolButton>
#include <QMessageBox>
#include <QMouseEvent>
#include <QLabel>
#include <QmitkAboutDialog.h>
QmitkExtWorkbenchWindowAdvisorHack* QmitkExtWorkbenchWindowAdvisorHack::undohack =
new QmitkExtWorkbenchWindowAdvisorHack();
QString QmitkExtWorkbenchWindowAdvisor::QT_SETTINGS_FILENAME = "QtSettings.ini";
static bool USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS = false;
class PartListenerForTitle: public berry::IPartListener
{
public:
PartListenerForTitle(QmitkExtWorkbenchWindowAdvisor* wa)
: windowAdvisor(wa)
{
}
Events::Types GetPartEventTypes() const override
{
return Events::ACTIVATED | Events::BROUGHT_TO_TOP | Events::CLOSED
| Events::HIDDEN | Events::VISIBLE;
}
void PartActivated(const berry::IWorkbenchPartReference::Pointer& ref) override
{
if (ref.Cast<berry::IEditorReference> ())
{
windowAdvisor->UpdateTitle(false);
}
}
void PartBroughtToTop(const berry::IWorkbenchPartReference::Pointer& ref) override
{
if (ref.Cast<berry::IEditorReference> ())
{
windowAdvisor->UpdateTitle(false);
}
}
void PartClosed(const berry::IWorkbenchPartReference::Pointer& /*ref*/) override
{
windowAdvisor->UpdateTitle(false);
}
void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override
{
if (!windowAdvisor->lastActiveEditor.Expired() &&
ref->GetPart(false) == windowAdvisor->lastActiveEditor.Lock())
{
windowAdvisor->UpdateTitle(true);
}
}
void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override
{
if (!windowAdvisor->lastActiveEditor.Expired() &&
ref->GetPart(false) == windowAdvisor->lastActiveEditor.Lock())
{
windowAdvisor->UpdateTitle(false);
}
}
private:
QmitkExtWorkbenchWindowAdvisor* windowAdvisor;
};
class PartListenerForViewNavigator: public berry::IPartListener
{
public:
PartListenerForViewNavigator(QAction* act)
: viewNavigatorAction(act)
{
}
Events::Types GetPartEventTypes() const override
{
return Events::OPENED | Events::CLOSED | Events::HIDDEN |
Events::VISIBLE;
}
void PartOpened(const berry::IWorkbenchPartReference::Pointer& ref) override
{
if (ref->GetId()=="org.mitk.views.viewnavigator")
{
viewNavigatorAction->setChecked(true);
}
}
void PartClosed(const berry::IWorkbenchPartReference::Pointer& ref) override
{
if (ref->GetId()=="org.mitk.views.viewnavigator")
{
viewNavigatorAction->setChecked(false);
}
}
void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override
{
if (ref->GetId()=="org.mitk.views.viewnavigator")
{
viewNavigatorAction->setChecked(true);
}
}
void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override
{
if (ref->GetId()=="org.mitk.views.viewnavigator")
{
viewNavigatorAction->setChecked(false);
}
}
private:
QAction* viewNavigatorAction;
};
class PartListenerForImageNavigator: public berry::IPartListener
{
public:
PartListenerForImageNavigator(QAction* act)
: imageNavigatorAction(act)
{
}
Events::Types GetPartEventTypes() const override
{
return Events::OPENED | Events::CLOSED | Events::HIDDEN |
Events::VISIBLE;
}
void PartOpened(const berry::IWorkbenchPartReference::Pointer& ref) override
{
if (ref->GetId()=="org.mitk.views.imagenavigator")
{
imageNavigatorAction->setChecked(true);
}
}
void PartClosed(const berry::IWorkbenchPartReference::Pointer& ref) override
{
if (ref->GetId()=="org.mitk.views.imagenavigator")
{
imageNavigatorAction->setChecked(false);
}
}
void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override
{
if (ref->GetId()=="org.mitk.views.imagenavigator")
{
imageNavigatorAction->setChecked(true);
}
}
void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override
{
if (ref->GetId()=="org.mitk.views.imagenavigator")
{
imageNavigatorAction->setChecked(false);
}
}
private:
QAction* imageNavigatorAction;
};
class PerspectiveListenerForTitle: public berry::IPerspectiveListener
{
public:
PerspectiveListenerForTitle(QmitkExtWorkbenchWindowAdvisor* wa)
: windowAdvisor(wa)
, perspectivesClosed(false)
{
}
Events::Types GetPerspectiveEventTypes() const override
{
if (USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS)
{
return Events::ACTIVATED | Events::SAVED_AS | Events::DEACTIVATED;
}
else
{
return Events::ACTIVATED | Events::SAVED_AS | Events::DEACTIVATED
| Events::CLOSED | Events::OPENED;
}
}
void PerspectiveActivated(const berry::IWorkbenchPage::Pointer& /*page*/,
const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override
{
windowAdvisor->UpdateTitle(false);
}
void PerspectiveSavedAs(const berry::IWorkbenchPage::Pointer& /*page*/,
const berry::IPerspectiveDescriptor::Pointer& /*oldPerspective*/,
const berry::IPerspectiveDescriptor::Pointer& /*newPerspective*/) override
{
windowAdvisor->UpdateTitle(false);
}
void PerspectiveDeactivated(const berry::IWorkbenchPage::Pointer& /*page*/,
const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override
{
windowAdvisor->UpdateTitle(false);
}
void PerspectiveOpened(const berry::IWorkbenchPage::Pointer& /*page*/,
const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override
{
if (perspectivesClosed)
{
QListIterator<QAction*> i(windowAdvisor->viewActions);
while (i.hasNext())
{
i.next()->setEnabled(true);
}
//GetViewRegistry()->Find("org.mitk.views.imagenavigator");
if(windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicombrowser"))
{
windowAdvisor->openDicomEditorAction->setEnabled(true);
}
if (windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.stdmultiwidget"))
{
windowAdvisor->openStdMultiWidgetEditorAction->setEnabled(true);
}
if (windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.mxnmultiwidget"))
{
windowAdvisor->openMxNMultiWidgetEditorAction->setEnabled(true);
}
windowAdvisor->fileSaveProjectAction->setEnabled(true);
windowAdvisor->closeProjectAction->setEnabled(true);
windowAdvisor->undoAction->setEnabled(true);
windowAdvisor->redoAction->setEnabled(true);
windowAdvisor->imageNavigatorAction->setEnabled(true);
windowAdvisor->viewNavigatorAction->setEnabled(true);
windowAdvisor->resetPerspAction->setEnabled(true);
if( windowAdvisor->GetShowClosePerspectiveMenuItem() )
{
windowAdvisor->closePerspAction->setEnabled(true);
}
}
perspectivesClosed = false;
}
void PerspectiveClosed(const berry::IWorkbenchPage::Pointer& /*page*/,
const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override
{
berry::IWorkbenchWindow::Pointer wnd = windowAdvisor->GetWindowConfigurer()->GetWindow();
bool allClosed = true;
if (wnd->GetActivePage())
{
QList<berry::IPerspectiveDescriptor::Pointer> perspectives(wnd->GetActivePage()->GetOpenPerspectives());
allClosed = perspectives.empty();
}
if (allClosed)
{
perspectivesClosed = true;
QListIterator<QAction*> i(windowAdvisor->viewActions);
while (i.hasNext())
{
i.next()->setEnabled(false);
}
if(windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicombrowser"))
{
windowAdvisor->openDicomEditorAction->setEnabled(false);
}
if (windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.stdmultiwidget"))
{
windowAdvisor->openStdMultiWidgetEditorAction->setEnabled(false);
}
if (windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.mxnmultiwidget"))
{
windowAdvisor->openMxNMultiWidgetEditorAction->setEnabled(false);
}
windowAdvisor->fileSaveProjectAction->setEnabled(false);
windowAdvisor->closeProjectAction->setEnabled(false);
windowAdvisor->undoAction->setEnabled(false);
windowAdvisor->redoAction->setEnabled(false);
windowAdvisor->imageNavigatorAction->setEnabled(false);
windowAdvisor->viewNavigatorAction->setEnabled(false);
windowAdvisor->resetPerspAction->setEnabled(false);
if( windowAdvisor->GetShowClosePerspectiveMenuItem() )
{
windowAdvisor->closePerspAction->setEnabled(false);
}
}
}
private:
QmitkExtWorkbenchWindowAdvisor* windowAdvisor;
bool perspectivesClosed;
};
class PerspectiveListenerForMenu: public berry::IPerspectiveListener
{
public:
PerspectiveListenerForMenu(QmitkExtWorkbenchWindowAdvisor* wa)
: windowAdvisor(wa)
{
}
Events::Types GetPerspectiveEventTypes() const override
{
return Events::ACTIVATED | Events::DEACTIVATED;
}
void PerspectiveActivated(const berry::IWorkbenchPage::Pointer& /*page*/,
const berry::IPerspectiveDescriptor::Pointer& perspective) override
{
QAction* action = windowAdvisor->mapPerspIdToAction[perspective->GetId()];
if (action)
{
action->setChecked(true);
}
}
void PerspectiveDeactivated(const berry::IWorkbenchPage::Pointer& /*page*/,
const berry::IPerspectiveDescriptor::Pointer& perspective) override
{
QAction* action = windowAdvisor->mapPerspIdToAction[perspective->GetId()];
if (action)
{
action->setChecked(false);
}
}
private:
QmitkExtWorkbenchWindowAdvisor* windowAdvisor;
};
QmitkExtWorkbenchWindowAdvisor::QmitkExtWorkbenchWindowAdvisor(berry::WorkbenchAdvisor* wbAdvisor,
berry::IWorkbenchWindowConfigurer::Pointer configurer)
: berry::WorkbenchWindowAdvisor(configurer)
, lastInput(nullptr)
, wbAdvisor(wbAdvisor)
, showViewToolbar(true)
, showPerspectiveToolbar(false)
, showVersionInfo(true)
, showMitkVersionInfo(true)
, showViewMenuItem(true)
, showNewWindowMenuItem(false)
, showClosePerspectiveMenuItem(true)
, viewNavigatorFound(false)
, showMemoryIndicator(true)
, dropTargetListener(new QmitkDefaultDropTargetListener)
{
productName = QCoreApplication::applicationName();
viewExcludeList.push_back("org.mitk.views.viewnavigator");
}
QmitkExtWorkbenchWindowAdvisor::~QmitkExtWorkbenchWindowAdvisor()
{
}
berry::ActionBarAdvisor::Pointer QmitkExtWorkbenchWindowAdvisor::CreateActionBarAdvisor(berry::IActionBarConfigurer::Pointer configurer)
{
if (USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS)
{
berry::ActionBarAdvisor::Pointer actionBarAdvisor(new QmitkExtActionBarAdvisor(configurer));
return actionBarAdvisor;
}
else
{
return berry::WorkbenchWindowAdvisor::CreateActionBarAdvisor(configurer);
}
}
QWidget* QmitkExtWorkbenchWindowAdvisor::CreateEmptyWindowContents(QWidget* parent)
{
QWidget* parentWidget = static_cast<QWidget*>(parent);
auto label = new QLabel(parentWidget);
label->setText("<b>No perspectives are open. Open a perspective in the <i>Window->Open Perspective</i> menu.</b>");
label->setContentsMargins(10,10,10,10);
label->setAlignment(Qt::AlignTop);
label->setEnabled(false);
parentWidget->layout()->addWidget(label);
return label;
}
void QmitkExtWorkbenchWindowAdvisor::ShowClosePerspectiveMenuItem(bool show)
{
showClosePerspectiveMenuItem = show;
}
bool QmitkExtWorkbenchWindowAdvisor::GetShowClosePerspectiveMenuItem()
{
return showClosePerspectiveMenuItem;
}
void QmitkExtWorkbenchWindowAdvisor::ShowMemoryIndicator(bool show)
{
showMemoryIndicator = show;
}
bool QmitkExtWorkbenchWindowAdvisor::GetShowMemoryIndicator()
{
return showMemoryIndicator;
}
void QmitkExtWorkbenchWindowAdvisor::ShowNewWindowMenuItem(bool show)
{
showNewWindowMenuItem = show;
}
void QmitkExtWorkbenchWindowAdvisor::ShowViewToolbar(bool show)
{
showViewToolbar = show;
}
void QmitkExtWorkbenchWindowAdvisor::ShowViewMenuItem(bool show)
{
showViewMenuItem = show;
}
void QmitkExtWorkbenchWindowAdvisor::ShowPerspectiveToolbar(bool show)
{
showPerspectiveToolbar = show;
}
void QmitkExtWorkbenchWindowAdvisor::ShowVersionInfo(bool show)
{
showVersionInfo = show;
}
void QmitkExtWorkbenchWindowAdvisor::ShowMitkVersionInfo(bool show)
{
showMitkVersionInfo = show;
}
void QmitkExtWorkbenchWindowAdvisor::SetProductName(const QString& product)
{
productName = product;
}
void QmitkExtWorkbenchWindowAdvisor::SetWindowIcon(const QString& wndIcon)
{
windowIcon = wndIcon;
}
void QmitkExtWorkbenchWindowAdvisor::PostWindowCreate()
{
// very bad hack...
berry::IWorkbenchWindow::Pointer window = this->GetWindowConfigurer()->GetWindow();
QMainWindow* mainWindow = qobject_cast<QMainWindow*> (window->GetShell()->GetControl());
if (!windowIcon.isEmpty())
{
mainWindow->setWindowIcon(QIcon(windowIcon));
}
mainWindow->setContextMenuPolicy(Qt::PreventContextMenu);
// Load icon theme
QIcon::setThemeSearchPaths(QStringList() << QStringLiteral(":/org_mitk_icons/icons/"));
QIcon::setThemeName(QStringLiteral("awesome"));
// ==== Application menu ============================
QMenuBar* menuBar = mainWindow->menuBar();
menuBar->setContextMenuPolicy(Qt::PreventContextMenu);
#ifdef __APPLE__
menuBar->setNativeMenuBar(true);
#else
menuBar->setNativeMenuBar(false);
#endif
auto basePath = QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/");
auto fileOpenAction = new QmitkFileOpenAction(berry::QtStyleManager::ThemeIcon(basePath + "document-open.svg"), window);
fileOpenAction->setShortcut(QKeySequence::Open);
auto fileSaveAction = new QmitkFileSaveAction(berry::QtStyleManager::ThemeIcon(basePath + "document-save.svg"), window);
fileSaveAction->setShortcut(QKeySequence::Save);
fileSaveProjectAction = new QmitkExtFileSaveProjectAction(window);
fileSaveProjectAction->setIcon(berry::QtStyleManager::ThemeIcon(basePath + "document-save.svg"));
closeProjectAction = new QmitkCloseProjectAction(window);
closeProjectAction->setIcon(berry::QtStyleManager::ThemeIcon(basePath + "edit-delete.svg"));
auto perspGroup = new QActionGroup(menuBar);
std::map<QString, berry::IViewDescriptor::Pointer> VDMap;
// sort elements (converting vector to map...)
QList<berry::IViewDescriptor::Pointer>::const_iterator iter;
berry::IViewRegistry* viewRegistry =
berry::PlatformUI::GetWorkbench()->GetViewRegistry();
const QList<berry::IViewDescriptor::Pointer> viewDescriptors = viewRegistry->GetViews();
bool skip = false;
for (iter = viewDescriptors.begin(); iter != viewDescriptors.end(); ++iter)
{
// if viewExcludeList is set, it contains the id-strings of view, which
// should not appear as an menu-entry in the menu
if (viewExcludeList.size() > 0)
{
for (int i=0; i<viewExcludeList.size(); i++)
{
if (viewExcludeList.at(i) == (*iter)->GetId())
{
skip = true;
break;
}
}
if (skip)
{
skip = false;
continue;
}
}
if ((*iter)->GetId() == "org.blueberry.ui.internal.introview")
continue;
if ((*iter)->GetId() == "org.mitk.views.imagenavigator")
continue;
if ((*iter)->GetId() == "org.mitk.views.viewnavigator")
continue;
std::pair<QString, berry::IViewDescriptor::Pointer> p((*iter)->GetLabel(), (*iter));
VDMap.insert(p);
}
std::map<QString, berry::IViewDescriptor::Pointer>::const_iterator MapIter;
for (MapIter = VDMap.begin(); MapIter != VDMap.end(); ++MapIter)
{
berry::QtShowViewAction* viewAction = new berry::QtShowViewAction(window, (*MapIter).second);
viewActions.push_back(viewAction);
}
if (!USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS)
{
QMenu* fileMenu = menuBar->addMenu("&File");
fileMenu->setObjectName("FileMenu");
fileMenu->addAction(fileOpenAction);
fileMenu->addAction(fileSaveAction);
fileMenu->addAction(fileSaveProjectAction);
fileMenu->addAction(closeProjectAction);
fileMenu->addSeparator();
QAction* fileExitAction = new QmitkFileExitAction(window);
fileExitAction->setIcon(berry::QtStyleManager::ThemeIcon(basePath + "system-log-out.svg"));
fileExitAction->setShortcut(QKeySequence::Quit);
fileExitAction->setObjectName("QmitkFileExitAction");
fileMenu->addAction(fileExitAction);
// another bad hack to get an edit/undo menu...
QMenu* editMenu = menuBar->addMenu("&Edit");
undoAction = editMenu->addAction(berry::QtStyleManager::ThemeIcon(basePath + "edit-undo.svg"),
"&Undo",
QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onUndo()),
QKeySequence("CTRL+Z"));
undoAction->setToolTip("Undo the last action (not supported by all modules)");
redoAction = editMenu->addAction(berry::QtStyleManager::ThemeIcon(basePath + "edit-redo.svg"),
"&Redo",
QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onRedo()),
QKeySequence("CTRL+Y"));
redoAction->setToolTip("execute the last action that was undone again (not supported by all modules)");
// ==== Window Menu ==========================
QMenu* windowMenu = menuBar->addMenu("Window");
if (showNewWindowMenuItem)
{
windowMenu->addAction("&New Window", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onNewWindow()));
windowMenu->addSeparator();
}
QMenu* perspMenu = windowMenu->addMenu("&Open Perspective");
QMenu* viewMenu = nullptr;
if (showViewMenuItem)
{
viewMenu = windowMenu->addMenu("Show &View");
viewMenu->setObjectName("Show View");
}
windowMenu->addSeparator();
resetPerspAction = windowMenu->addAction("&Reset Perspective",
QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onResetPerspective()));
if(showClosePerspectiveMenuItem)
closePerspAction = windowMenu->addAction("&Close Perspective", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onClosePerspective()));
windowMenu->addSeparator();
windowMenu->addAction("&Preferences...",
QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onEditPreferences()),
QKeySequence("CTRL+P"));
// fill perspective menu
berry::IPerspectiveRegistry* perspRegistry =
window->GetWorkbench()->GetPerspectiveRegistry();
QList<berry::IPerspectiveDescriptor::Pointer> perspectives(
perspRegistry->GetPerspectives());
skip = false;
for (QList<berry::IPerspectiveDescriptor::Pointer>::iterator perspIt =
perspectives.begin(); perspIt != perspectives.end(); ++perspIt)
{
// if perspectiveExcludeList is set, it contains the id-strings of perspectives, which
// should not appear as an menu-entry in the perspective menu
if (perspectiveExcludeList.size() > 0)
{
for (int i=0; i<perspectiveExcludeList.size(); i++)
{
if (perspectiveExcludeList.at(i) == (*perspIt)->GetId())
{
skip = true;
break;
}
}
if (skip)
{
skip = false;
continue;
}
}
QAction* perspAction = new berry::QtOpenPerspectiveAction(window, *perspIt, perspGroup);
mapPerspIdToAction.insert((*perspIt)->GetId(), perspAction);
}
perspMenu->addActions(perspGroup->actions());
if (showViewMenuItem)
{
for (auto viewAction : qAsConst(viewActions))
{
viewMenu->addAction(viewAction);
}
}
// ===== Help menu ====================================
QMenu* helpMenu = menuBar->addMenu("&Help");
helpMenu->addAction("&Welcome",this, SLOT(onIntro()));
helpMenu->addAction("&Open Help Perspective", this, SLOT(onHelpOpenHelpPerspective()));
helpMenu->addAction("&Context Help",this, SLOT(onHelp()), QKeySequence("F1"));
helpMenu->addAction("&About",this, SLOT(onAbout()));
// =====================================================
}
else
{
undoAction = new QmitkUndoAction(berry::QtStyleManager::ThemeIcon(basePath + "edit-undo.svg"), nullptr);
undoAction->setShortcut(QKeySequence::Undo);
redoAction = new QmitkRedoAction(berry::QtStyleManager::ThemeIcon(basePath + "edit-redo.svg"), nullptr);
redoAction->setShortcut(QKeySequence::Redo);
}
// toolbar for showing file open, undo, redo and other main actions
auto mainActionsToolBar = new QToolBar;
mainActionsToolBar->setObjectName("mainActionsToolBar");
mainActionsToolBar->setContextMenuPolicy(Qt::PreventContextMenu);
#ifdef __APPLE__
mainActionsToolBar->setToolButtonStyle ( Qt::ToolButtonTextUnderIcon );
#else
mainActionsToolBar->setToolButtonStyle ( Qt::ToolButtonTextBesideIcon );
#endif
basePath = QStringLiteral(":/org.mitk.gui.qt.ext/");
imageNavigatorAction = new QAction(berry::QtStyleManager::ThemeIcon(basePath + "image_navigator.svg"), "&Image Navigator", nullptr);
bool imageNavigatorViewFound = window->GetWorkbench()->GetViewRegistry()->Find("org.mitk.views.imagenavigator");
if (this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicombrowser"))
{
openDicomEditorAction = new QmitkOpenDicomEditorAction(berry::QtStyleManager::ThemeIcon(basePath + "dicom.svg"), window);
}
if (this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.stdmultiwidget"))
{
openStdMultiWidgetEditorAction = new QmitkOpenStdMultiWidgetEditorAction(QIcon(":/org.mitk.gui.qt.ext/Editor.png"), window);
}
if (this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.mxnmultiwidget"))
{
openMxNMultiWidgetEditorAction = new QmitkOpenMxNMultiWidgetEditorAction(QIcon(":/org.mitk.gui.qt.ext/Editor.png"), window);
}
if (imageNavigatorViewFound)
{
QObject::connect(imageNavigatorAction, SIGNAL(triggered(bool)), QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onImageNavigator()));
imageNavigatorAction->setCheckable(true);
// add part listener for image navigator
imageNavigatorPartListener.reset(new PartListenerForImageNavigator(imageNavigatorAction));
window->GetPartService()->AddPartListener(imageNavigatorPartListener.data());
berry::IViewPart::Pointer imageNavigatorView = window->GetActivePage()->FindView("org.mitk.views.imagenavigator");
imageNavigatorAction->setChecked(false);
if (imageNavigatorView)
{
bool isImageNavigatorVisible = window->GetActivePage()->IsPartVisible(imageNavigatorView);
if (isImageNavigatorVisible)
imageNavigatorAction->setChecked(true);
}
imageNavigatorAction->setToolTip("Toggle image navigator for navigating through image");
}
viewNavigatorAction = new QAction(berry::QtStyleManager::ThemeIcon(QStringLiteral(":/org.mitk.gui.qt.ext/view-manager.svg")),"&View Navigator", nullptr);
viewNavigatorFound = window->GetWorkbench()->GetViewRegistry()->Find("org.mitk.views.viewnavigator");
if (viewNavigatorFound)
{
QObject::connect(viewNavigatorAction, SIGNAL(triggered(bool)), QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onViewNavigator()));
viewNavigatorAction->setCheckable(true);
// add part listener for view navigator
viewNavigatorPartListener.reset(new PartListenerForViewNavigator(viewNavigatorAction));
window->GetPartService()->AddPartListener(viewNavigatorPartListener.data());
berry::IViewPart::Pointer viewnavigatorview = window->GetActivePage()->FindView("org.mitk.views.viewnavigator");
viewNavigatorAction->setChecked(false);
if (viewnavigatorview)
{
bool isViewNavigatorVisible = window->GetActivePage()->IsPartVisible(viewnavigatorview);
if (isViewNavigatorVisible)
viewNavigatorAction->setChecked(true);
}
viewNavigatorAction->setToolTip("Toggle View Navigator");
}
mainActionsToolBar->addAction(fileOpenAction);
mainActionsToolBar->addAction(fileSaveProjectAction);
mainActionsToolBar->addAction(closeProjectAction);
mainActionsToolBar->addAction(undoAction);
mainActionsToolBar->addAction(redoAction);
if(this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicombrowser"))
{
mainActionsToolBar->addAction(openDicomEditorAction);
}
if (this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.stdmultiwidget"))
{
mainActionsToolBar->addAction(openStdMultiWidgetEditorAction);
}
if (this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.mxnmultiwidget"))
{
mainActionsToolBar->addAction(openMxNMultiWidgetEditorAction);
}
if (imageNavigatorViewFound)
{
mainActionsToolBar->addAction(imageNavigatorAction);
}
if (viewNavigatorFound)
{
mainActionsToolBar->addAction(viewNavigatorAction);
}
mainWindow->addToolBar(mainActionsToolBar);
// ==== Perspective Toolbar ==================================
auto qPerspectiveToolbar = new QToolBar;
qPerspectiveToolbar->setObjectName("perspectiveToolBar");
if (showPerspectiveToolbar)
{
qPerspectiveToolbar->addActions(perspGroup->actions());
mainWindow->addToolBar(qPerspectiveToolbar);
}
else
delete qPerspectiveToolbar;
if (showViewToolbar)
{
auto prefService = berry::WorkbenchPlugin::GetDefault()->GetPreferencesService();
berry::IPreferences::Pointer stylePrefs = prefService->GetSystemPreferences()->Node(berry::QtPreferences::QT_STYLES_NODE);
bool showCategoryNames = stylePrefs->GetBool(berry::QtPreferences::QT_SHOW_TOOLBAR_CATEGORY_NAMES, true);
// Order view descriptors by category
QMultiMap<QString, berry::IViewDescriptor::Pointer> categoryViewDescriptorMap;
for (const auto &labelViewDescriptorPair : VDMap)
{
auto viewDescriptor = labelViewDescriptorPair.second;
auto category = !viewDescriptor->GetCategoryPath().isEmpty()
? viewDescriptor->GetCategoryPath().back()
: QString();
categoryViewDescriptorMap.insert(category, viewDescriptor);
}
// Create a separate toolbar for each category
for (const auto &category : categoryViewDescriptorMap.uniqueKeys())
{
auto viewDescriptorsInCurrentCategory = categoryViewDescriptorMap.values(category);
if (!viewDescriptorsInCurrentCategory.isEmpty())
{
auto toolbar = new QToolBar;
toolbar->setObjectName(category + " View Toolbar");
mainWindow->addToolBar(toolbar);
if (showCategoryNames && !category.isEmpty())
{
auto categoryButton = new QToolButton;
categoryButton->setToolButtonStyle(Qt::ToolButtonTextOnly);
categoryButton->setText(category);
categoryButton->setStyleSheet("background: transparent; margin: 0; padding: 0;");
toolbar->addWidget(categoryButton);
connect(categoryButton, &QToolButton::clicked, [toolbar]()
{
for (QWidget* widget : toolbar->findChildren<QWidget*>())
{
if (QStringLiteral("qt_toolbar_ext_button") == widget->objectName() && widget->isVisible())
{
QMouseEvent pressEvent(QEvent::MouseButtonPress, QPointF(0.0f, 0.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPointF(0.0f, 0.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QApplication::sendEvent(widget, &pressEvent);
QApplication::sendEvent(widget, &releaseEvent);
}
}
});
}
for (const auto &viewDescriptor : qAsConst(viewDescriptorsInCurrentCategory))
{
auto viewAction = new berry::QtShowViewAction(window, viewDescriptor);
toolbar->addAction(viewAction);
}
}
}
}
QSettings settings(GetQSettingsFile(), QSettings::IniFormat);
mainWindow->restoreState(settings.value("ToolbarPosition").toByteArray());
auto qStatusBar = new QStatusBar();
//creating a QmitkStatusBar for Output on the QStatusBar and connecting it with the MainStatusBar
auto statusBar = new QmitkStatusBar(qStatusBar);
//disabling the SizeGrip in the lower right corner
statusBar->SetSizeGripEnabled(false);
auto progBar = new QmitkProgressBar();
qStatusBar->addPermanentWidget(progBar, 0);
progBar->hide();
// progBar->AddStepsToDo(2);
// progBar->Progress(1);
mainWindow->setStatusBar(qStatusBar);
if (showMemoryIndicator)
{
auto memoryIndicator = new QmitkMemoryUsageIndicatorView();
qStatusBar->addPermanentWidget(memoryIndicator, 0);
}
}
void QmitkExtWorkbenchWindowAdvisor::PreWindowOpen()
{
berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer();
// show the shortcut bar and progress indicator, which are hidden by
// default
//configurer->SetShowPerspectiveBar(true);
//configurer->SetShowFastViewBars(true);
//configurer->SetShowProgressIndicator(true);
// // add the drag and drop support for the editor area
// configurer.addEditorAreaTransfer(EditorInputTransfer.getInstance());
// configurer.addEditorAreaTransfer(ResourceTransfer.getInstance());
// configurer.addEditorAreaTransfer(FileTransfer.getInstance());
// configurer.addEditorAreaTransfer(MarkerTransfer.getInstance());
// configurer.configureEditorAreaDropListener(new EditorAreaDropAdapter(
// configurer.getWindow()));
this->HookTitleUpdateListeners(configurer);
menuPerspectiveListener.reset(new PerspectiveListenerForMenu(this));
configurer->GetWindow()->AddPerspectiveListener(menuPerspectiveListener.data());
configurer->AddEditorAreaTransfer(QStringList("text/uri-list"));
configurer->ConfigureEditorAreaDropListener(dropTargetListener.data());
}
void QmitkExtWorkbenchWindowAdvisor::PostWindowOpen()
{
berry::WorkbenchWindowAdvisor::PostWindowOpen();
// Force Rendering Window Creation on startup.
berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer();
ctkPluginContext* context = QmitkCommonExtPlugin::getContext();
ctkServiceReference serviceRef = context->getServiceReference<mitk::IDataStorageService>();
if (serviceRef)
{
mitk::IDataStorageService *dsService = context->getService<mitk::IDataStorageService>(serviceRef);
if (dsService)
{
mitk::IDataStorageReference::Pointer dsRef = dsService->GetDataStorage();
mitk::DataStorageEditorInput::Pointer dsInput(new mitk::DataStorageEditorInput(dsRef));
mitk::WorkbenchUtil::OpenEditor(configurer->GetWindow()->GetActivePage(),dsInput);
}
}
auto introPart = configurer->GetWindow()->GetWorkbench()->GetIntroManager()->GetIntro();
if (introPart.IsNotNull())
{
configurer->GetWindow()->GetWorkbench()->GetIntroManager()->ShowIntro(GetWindowConfigurer()->GetWindow(), false);
}
}
void QmitkExtWorkbenchWindowAdvisor::onIntro()
{
QmitkExtWorkbenchWindowAdvisorHack::undohack->onIntro();
}
void QmitkExtWorkbenchWindowAdvisor::onHelp()
{
QmitkExtWorkbenchWindowAdvisorHack::undohack->onHelp();
}
void QmitkExtWorkbenchWindowAdvisor::onHelpOpenHelpPerspective()
{
QmitkExtWorkbenchWindowAdvisorHack::undohack->onHelpOpenHelpPerspective();
}
void QmitkExtWorkbenchWindowAdvisor::onAbout()
{
QmitkExtWorkbenchWindowAdvisorHack::undohack->onAbout();
}
//--------------------------------------------------------------------------------
// Ugly hack from here on. Feel free to delete when command framework
// and undo buttons are done.
//--------------------------------------------------------------------------------
QmitkExtWorkbenchWindowAdvisorHack::QmitkExtWorkbenchWindowAdvisorHack()
: QObject()
{
}
QmitkExtWorkbenchWindowAdvisorHack::~QmitkExtWorkbenchWindowAdvisorHack()
{
}
void QmitkExtWorkbenchWindowAdvisorHack::onUndo()
{
mitk::UndoModel* model = mitk::UndoController::GetCurrentUndoModel();
if (model)
{
if (mitk::VerboseLimitedLinearUndo* verboseundo = dynamic_cast<mitk::VerboseLimitedLinearUndo*>( model ))
{
mitk::VerboseLimitedLinearUndo::StackDescription descriptions = verboseundo->GetUndoDescriptions();
if (descriptions.size() >= 1)
{
MITK_INFO << "Undo " << descriptions.front().second;
}
}
model->Undo();
}
else
{
MITK_ERROR << "No undo model instantiated";
}
}
void QmitkExtWorkbenchWindowAdvisorHack::onRedo()
{
mitk::UndoModel* model = mitk::UndoController::GetCurrentUndoModel();
if (model)
{
if (mitk::VerboseLimitedLinearUndo* verboseundo = dynamic_cast<mitk::VerboseLimitedLinearUndo*>( model ))
{
mitk::VerboseLimitedLinearUndo::StackDescription descriptions = verboseundo->GetRedoDescriptions();
if (descriptions.size() >= 1)
{
MITK_INFO << "Redo " << descriptions.front().second;
}
}
model->Redo();
}
else
{
MITK_ERROR << "No undo model instantiated";
}
}
// safe calls to the complete chain
// berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->FindView("org.mitk.views.imagenavigator");
// to cover for all possible cases of closed pages etc.
static void SafeHandleNavigatorView(QString view_query_name)
{
berry::IWorkbench* wbench = berry::PlatformUI::GetWorkbench();
if( wbench == nullptr )
return;
berry::IWorkbenchWindow::Pointer wbench_window = wbench->GetActiveWorkbenchWindow();
if( wbench_window.IsNull() )
return;
berry::IWorkbenchPage::Pointer wbench_page = wbench_window->GetActivePage();
if( wbench_page.IsNull() )
return;
auto wbench_view = wbench_page->FindView( view_query_name );
if( wbench_view.IsNotNull() )
{
bool isViewVisible = wbench_page->IsPartVisible( wbench_view );
if( isViewVisible )
{
wbench_page->HideView( wbench_view );
return;
}
}
wbench_page->ShowView( view_query_name );
}
void QmitkExtWorkbenchWindowAdvisorHack::onImageNavigator()
{
// show/hide ImageNavigatorView
SafeHandleNavigatorView("org.mitk.views.imagenavigator");
}
void QmitkExtWorkbenchWindowAdvisorHack::onViewNavigator()
{
// show/hide viewnavigatorView
SafeHandleNavigatorView("org.mitk.views.viewnavigator");
}
void QmitkExtWorkbenchWindowAdvisorHack::onEditPreferences()
{
QmitkPreferencesDialog _PreferencesDialog(QApplication::activeWindow());
_PreferencesDialog.exec();
}
void QmitkExtWorkbenchWindowAdvisorHack::onQuit()
{
berry::PlatformUI::GetWorkbench()->Close();
}
void QmitkExtWorkbenchWindowAdvisorHack::onResetPerspective()
{
berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->ResetPerspective();
}
void QmitkExtWorkbenchWindowAdvisorHack::onClosePerspective()
{
berry::IWorkbenchPage::Pointer page =
berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage();
page->ClosePerspective(page->GetPerspective(), true, true);
}
void QmitkExtWorkbenchWindowAdvisorHack::onNewWindow()
{
berry::PlatformUI::GetWorkbench()->OpenWorkbenchWindow(nullptr);
}
void QmitkExtWorkbenchWindowAdvisorHack::onIntro()
{
bool hasIntro =
berry::PlatformUI::GetWorkbench()->GetIntroManager()->HasIntro();
if (!hasIntro)
{
QRegExp reg("(.*)<title>(\\n)*");
QRegExp reg2("(\\n)*</title>(.*)");
QFile file(":/org.mitk.gui.qt.ext/index.html");
file.open(QIODevice::ReadOnly | QIODevice::Text); //text file only for reading
QString text = QString(file.readAll());
file.close();
QString title = text;
title.replace(reg, "");
title.replace(reg2, "");
std::cout << title.toStdString() << std::endl;
QMessageBox::information(nullptr, title,
text, "Close");
}
else
{
berry::PlatformUI::GetWorkbench()->GetIntroManager()->ShowIntro(
berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow(), false);
}
}
void QmitkExtWorkbenchWindowAdvisorHack::onHelp()
{
ctkPluginContext* context = QmitkCommonExtPlugin::getContext();
if (context == nullptr)
{
MITK_WARN << "Plugin context not set, unable to open context help";
return;
}
// Check if the org.blueberry.ui.qt.help plug-in is installed and started
QList<QSharedPointer<ctkPlugin> > plugins = context->getPlugins();
foreach(QSharedPointer<ctkPlugin> p, plugins)
{
if (p->getSymbolicName() == "org.blueberry.ui.qt.help")
{
if (p->getState() != ctkPlugin::ACTIVE)
{
// try to activate the plug-in explicitly
try
{
p->start(ctkPlugin::START_TRANSIENT);
}
catch (const ctkPluginException& pe)
{
MITK_ERROR << "Activating org.blueberry.ui.qt.help failed: " << pe.what();
return;
}
}
}
}
ctkServiceReference eventAdminRef = context->getServiceReference<ctkEventAdmin>();
ctkEventAdmin* eventAdmin = nullptr;
if (eventAdminRef)
{
eventAdmin = context->getService<ctkEventAdmin>(eventAdminRef);
}
if (eventAdmin == nullptr)
{
MITK_WARN << "ctkEventAdmin service not found. Unable to open context help";
}
else
{
ctkEvent ev("org/blueberry/ui/help/CONTEXTHELP_REQUESTED");
eventAdmin->postEvent(ev);
}
}
void QmitkExtWorkbenchWindowAdvisorHack::onHelpOpenHelpPerspective()
{
berry::PlatformUI::GetWorkbench()->ShowPerspective("org.blueberry.perspectives.help",
berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow());
}
void QmitkExtWorkbenchWindowAdvisorHack::onAbout()
{
auto aboutDialog = new QmitkAboutDialog(QApplication::activeWindow(),nullptr);
aboutDialog->open();
}
void QmitkExtWorkbenchWindowAdvisor::HookTitleUpdateListeners(berry::IWorkbenchWindowConfigurer::Pointer configurer)
{
// hook up the listeners to update the window title
titlePartListener.reset(new PartListenerForTitle(this));
titlePerspectiveListener.reset(new PerspectiveListenerForTitle(this));
editorPropertyListener.reset(new berry::PropertyChangeIntAdapter<
QmitkExtWorkbenchWindowAdvisor>(this,
&QmitkExtWorkbenchWindowAdvisor::PropertyChange));
// configurer.getWindow().addPageListener(new IPageListener() {
// public void pageActivated(IWorkbenchPage page) {
// updateTitle(false);
// }
//
// public void pageClosed(IWorkbenchPage page) {
// updateTitle(false);
// }
//
// public void pageOpened(IWorkbenchPage page) {
// // do nothing
// }
// });
configurer->GetWindow()->AddPerspectiveListener(titlePerspectiveListener.data());
configurer->GetWindow()->GetPartService()->AddPartListener(titlePartListener.data());
}
QString QmitkExtWorkbenchWindowAdvisor::ComputeTitle()
{
berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer();
berry::IWorkbenchPage::Pointer currentPage = configurer->GetWindow()->GetActivePage();
berry::IEditorPart::Pointer activeEditor;
if (currentPage)
{
activeEditor = lastActiveEditor.Lock();
}
QString title;
berry::IProduct::Pointer product = berry::Platform::GetProduct();
if (product.IsNotNull())
{
title = product->GetName();
}
if (title.isEmpty())
{
// instead of the product name, we use a custom variable for now
title = productName;
}
if(showMitkVersionInfo)
{
QString mitkVersionInfo = MITK_REVISION_DESC;
if(mitkVersionInfo.isEmpty())
mitkVersionInfo = MITK_VERSION_STRING;
title += " " + mitkVersionInfo;
}
if (showVersionInfo)
{
// add version informatioin
QString versions = QString(" (ITK %1.%2.%3 | VTK %4.%5.%6 | Qt %7)")
.arg(ITK_VERSION_MAJOR).arg(ITK_VERSION_MINOR).arg(ITK_VERSION_PATCH)
.arg(VTK_MAJOR_VERSION).arg(VTK_MINOR_VERSION).arg(VTK_BUILD_VERSION)
.arg(QT_VERSION_STR);
title += versions;
}
if (currentPage)
{
if (activeEditor)
{
lastEditorTitle = activeEditor->GetTitleToolTip();
if (!lastEditorTitle.isEmpty())
title = lastEditorTitle + " - " + title;
}
berry::IPerspectiveDescriptor::Pointer persp = currentPage->GetPerspective();
QString label = "";
if (persp)
{
label = persp->GetLabel();
}
berry::IAdaptable* input = currentPage->GetInput();
if (input && input != wbAdvisor->GetDefaultPageInput())
{
label = currentPage->GetLabel();
}
if (!label.isEmpty())
{
title = label + " - " + title;
}
}
title += " (Not for use in diagnosis or treatment of patients)";
return title;
}
void QmitkExtWorkbenchWindowAdvisor::RecomputeTitle()
{
berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer();
QString oldTitle = configurer->GetTitle();
QString newTitle = ComputeTitle();
if (newTitle != oldTitle)
{
configurer->SetTitle(newTitle);
}
}
void QmitkExtWorkbenchWindowAdvisor::UpdateTitle(bool editorHidden)
{
berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer();
berry::IWorkbenchWindow::Pointer window = configurer->GetWindow();
berry::IEditorPart::Pointer activeEditor;
berry::IWorkbenchPage::Pointer currentPage = window->GetActivePage();
berry::IPerspectiveDescriptor::Pointer persp;
berry::IAdaptable* input = nullptr;
if (currentPage)
{
activeEditor = currentPage->GetActiveEditor();
persp = currentPage->GetPerspective();
input = currentPage->GetInput();
}
if (editorHidden)
{
activeEditor = nullptr;
}
// Nothing to do if the editor hasn't changed
if (activeEditor == lastActiveEditor.Lock() && currentPage == lastActivePage.Lock()
&& persp == lastPerspective.Lock() && input == lastInput)
{
return;
}
if (!lastActiveEditor.Expired())
{
lastActiveEditor.Lock()->RemovePropertyListener(editorPropertyListener.data());
}
lastActiveEditor = activeEditor;
lastActivePage = currentPage;
lastPerspective = persp;
lastInput = input;
if (activeEditor)
{
activeEditor->AddPropertyListener(editorPropertyListener.data());
}
RecomputeTitle();
}
void QmitkExtWorkbenchWindowAdvisor::PropertyChange(const berry::Object::Pointer& /*source*/, int propId)
{
if (propId == berry::IWorkbenchPartConstants::PROP_TITLE)
{
if (!lastActiveEditor.Expired())
{
QString newTitle = lastActiveEditor.Lock()->GetPartName();
if (lastEditorTitle != newTitle)
{
RecomputeTitle();
}
}
}
}
void QmitkExtWorkbenchWindowAdvisor::SetPerspectiveExcludeList(const QList<QString>& v)
{
this->perspectiveExcludeList = v;
}
QList<QString> QmitkExtWorkbenchWindowAdvisor::GetPerspectiveExcludeList()
{
return this->perspectiveExcludeList;
}
void QmitkExtWorkbenchWindowAdvisor::SetViewExcludeList(const QList<QString>& v)
{
this->viewExcludeList = v;
}
QList<QString> QmitkExtWorkbenchWindowAdvisor::GetViewExcludeList()
{
return this->viewExcludeList;
}
void QmitkExtWorkbenchWindowAdvisor::PostWindowClose()
{
berry::IWorkbenchWindow::Pointer window = this->GetWindowConfigurer()->GetWindow();
QMainWindow* mainWindow = static_cast<QMainWindow*> (window->GetShell()->GetControl());
QSettings settings(GetQSettingsFile(), QSettings::IniFormat);
settings.setValue("ToolbarPosition", mainWindow->saveState());
}
QString QmitkExtWorkbenchWindowAdvisor::GetQSettingsFile() const
{
QFileInfo settingsInfo = QmitkCommonExtPlugin::getContext()->getDataFile(QT_SETTINGS_FILENAME);
return settingsInfo.canonicalFilePath();
}
diff --git a/Plugins/org.mitk.gui.qt.flowapplication/src/internal/QmitkFlowApplicationWorkbenchWindowAdvisor.cpp b/Plugins/org.mitk.gui.qt.flowapplication/src/internal/QmitkFlowApplicationWorkbenchWindowAdvisor.cpp
index 7b2e41bc87..710e4ebd81 100644
--- a/Plugins/org.mitk.gui.qt.flowapplication/src/internal/QmitkFlowApplicationWorkbenchWindowAdvisor.cpp
+++ b/Plugins/org.mitk.gui.qt.flowapplication/src/internal/QmitkFlowApplicationWorkbenchWindowAdvisor.cpp
@@ -1,1146 +1,1145 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "QmitkFlowApplicationWorkbenchWindowAdvisor.h"
#include <QMenu>
#include <QMenuBar>
#include <QMainWindow>
#include <QStatusBar>
#include <QString>
#include <QFile>
#include <QRegExp>
#include <QTextStream>
#include <QSettings>
#include <ctkPluginException.h>
#include <service/event/ctkEventAdmin.h>
#include <berryPlatform.h>
#include <berryPlatformUI.h>
#include <berryIActionBarConfigurer.h>
#include <berryIWorkbenchWindow.h>
#include <berryIWorkbenchPage.h>
#include <berryIPreferencesService.h>
#include <berryIPerspectiveRegistry.h>
#include <berryIPerspectiveDescriptor.h>
#include <berryIProduct.h>
#include <berryIWorkbenchPartConstants.h>
#include <berryQtPreferences.h>
#include <berryQtStyleManager.h>
#include <berryWorkbenchPlugin.h>
#include <berryIPreferences.h>
#include <internal/berryQtShowViewAction.h>
#include <internal/berryQtOpenPerspectiveAction.h>
#include <QmitkFileExitAction.h>
#include <QmitkCloseProjectAction.h>
#include <QmitkUndoAction.h>
#include <QmitkRedoAction.h>
#include <QmitkDefaultDropTargetListener.h>
#include <QmitkStatusBar.h>
#include <QmitkProgressBar.h>
#include <QmitkMemoryUsageIndicatorView.h>
#include <QmitkPreferencesDialog.h>
#include "QmitkExtFileSaveProjectAction.h"
#include <itkConfigure.h>
-#include <vtkConfigure.h>
#include <mitkVersion.h>
#include <mitkIDataStorageService.h>
#include <mitkIDataStorageReference.h>
#include <mitkDataStorageEditorInput.h>
#include <mitkWorkbenchUtil.h>
#include <vtkVersionMacros.h>
// UGLYYY
#include "QmitkFlowApplicationWorkbenchWindowAdvisorHack.h"
#include "QmitkFlowApplicationPlugin.h"
#include "mitkUndoController.h"
#include "mitkVerboseLimitedLinearUndo.h"
#include <QToolBar>
#include <QToolButton>
#include <QMessageBox>
#include <QMouseEvent>
#include <QLabel>
#include <QmitkAboutDialog.h>
QmitkFlowApplicationWorkbenchWindowAdvisorHack* QmitkFlowApplicationWorkbenchWindowAdvisorHack::undohack =
new QmitkFlowApplicationWorkbenchWindowAdvisorHack();
QString QmitkFlowApplicationWorkbenchWindowAdvisor::QT_SETTINGS_FILENAME = "QtSettings.ini";
class PartListenerForTitle: public berry::IPartListener
{
public:
PartListenerForTitle(QmitkFlowApplicationWorkbenchWindowAdvisor* wa)
: windowAdvisor(wa)
{
}
Events::Types GetPartEventTypes() const override
{
return Events::ACTIVATED | Events::BROUGHT_TO_TOP | Events::CLOSED
| Events::HIDDEN | Events::VISIBLE;
}
void PartActivated(const berry::IWorkbenchPartReference::Pointer& ref) override
{
if (ref.Cast<berry::IEditorReference> ())
{
windowAdvisor->UpdateTitle(false);
}
}
void PartBroughtToTop(const berry::IWorkbenchPartReference::Pointer& ref) override
{
if (ref.Cast<berry::IEditorReference> ())
{
windowAdvisor->UpdateTitle(false);
}
}
void PartClosed(const berry::IWorkbenchPartReference::Pointer& /*ref*/) override
{
windowAdvisor->UpdateTitle(false);
}
void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override
{
if (!windowAdvisor->lastActiveEditor.Expired() &&
ref->GetPart(false) == windowAdvisor->lastActiveEditor.Lock())
{
windowAdvisor->UpdateTitle(true);
}
}
void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override
{
if (!windowAdvisor->lastActiveEditor.Expired() &&
ref->GetPart(false) == windowAdvisor->lastActiveEditor.Lock())
{
windowAdvisor->UpdateTitle(false);
}
}
private:
QmitkFlowApplicationWorkbenchWindowAdvisor* windowAdvisor;
};
class PartListenerForImageNavigator: public berry::IPartListener
{
public:
PartListenerForImageNavigator(QAction* act)
: imageNavigatorAction(act)
{
}
Events::Types GetPartEventTypes() const override
{
return Events::OPENED | Events::CLOSED | Events::HIDDEN |
Events::VISIBLE;
}
void PartOpened(const berry::IWorkbenchPartReference::Pointer& ref) override
{
if (ref->GetId()=="org.mitk.views.imagenavigator")
{
imageNavigatorAction->setChecked(true);
}
}
void PartClosed(const berry::IWorkbenchPartReference::Pointer& ref) override
{
if (ref->GetId()=="org.mitk.views.imagenavigator")
{
imageNavigatorAction->setChecked(false);
}
}
void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override
{
if (ref->GetId()=="org.mitk.views.imagenavigator")
{
imageNavigatorAction->setChecked(true);
}
}
void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override
{
if (ref->GetId()=="org.mitk.views.imagenavigator")
{
imageNavigatorAction->setChecked(false);
}
}
private:
QAction* imageNavigatorAction;
};
class PerspectiveListenerForTitle: public berry::IPerspectiveListener
{
public:
PerspectiveListenerForTitle(QmitkFlowApplicationWorkbenchWindowAdvisor* wa)
: windowAdvisor(wa)
, perspectivesClosed(false)
{
}
Events::Types GetPerspectiveEventTypes() const override
{
return Events::ACTIVATED | Events::SAVED_AS | Events::DEACTIVATED
| Events::CLOSED | Events::OPENED;
}
void PerspectiveActivated(const berry::IWorkbenchPage::Pointer& /*page*/,
const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override
{
windowAdvisor->UpdateTitle(false);
}
void PerspectiveSavedAs(const berry::IWorkbenchPage::Pointer& /*page*/,
const berry::IPerspectiveDescriptor::Pointer& /*oldPerspective*/,
const berry::IPerspectiveDescriptor::Pointer& /*newPerspective*/) override
{
windowAdvisor->UpdateTitle(false);
}
void PerspectiveDeactivated(const berry::IWorkbenchPage::Pointer& /*page*/,
const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override
{
windowAdvisor->UpdateTitle(false);
}
void PerspectiveOpened(const berry::IWorkbenchPage::Pointer& /*page*/,
const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override
{
if (perspectivesClosed)
{
QListIterator<QAction*> i(windowAdvisor->viewActions);
while (i.hasNext())
{
i.next()->setEnabled(true);
}
windowAdvisor->fileSaveProjectAction->setEnabled(true);
windowAdvisor->undoAction->setEnabled(true);
windowAdvisor->redoAction->setEnabled(true);
windowAdvisor->imageNavigatorAction->setEnabled(true);
windowAdvisor->resetPerspAction->setEnabled(true);
}
perspectivesClosed = false;
}
void PerspectiveClosed(const berry::IWorkbenchPage::Pointer& /*page*/,
const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override
{
berry::IWorkbenchWindow::Pointer wnd = windowAdvisor->GetWindowConfigurer()->GetWindow();
bool allClosed = true;
if (wnd->GetActivePage())
{
QList<berry::IPerspectiveDescriptor::Pointer> perspectives(wnd->GetActivePage()->GetOpenPerspectives());
allClosed = perspectives.empty();
}
if (allClosed)
{
perspectivesClosed = true;
QListIterator<QAction*> i(windowAdvisor->viewActions);
while (i.hasNext())
{
i.next()->setEnabled(false);
}
windowAdvisor->fileSaveProjectAction->setEnabled(false);
windowAdvisor->undoAction->setEnabled(false);
windowAdvisor->redoAction->setEnabled(false);
windowAdvisor->imageNavigatorAction->setEnabled(false);
windowAdvisor->resetPerspAction->setEnabled(false);
}
}
private:
QmitkFlowApplicationWorkbenchWindowAdvisor* windowAdvisor;
bool perspectivesClosed;
};
class PerspectiveListenerForMenu: public berry::IPerspectiveListener
{
public:
PerspectiveListenerForMenu(QmitkFlowApplicationWorkbenchWindowAdvisor* wa)
: windowAdvisor(wa)
{
}
Events::Types GetPerspectiveEventTypes() const override
{
return Events::ACTIVATED | Events::DEACTIVATED;
}
void PerspectiveActivated(const berry::IWorkbenchPage::Pointer& /*page*/,
const berry::IPerspectiveDescriptor::Pointer& perspective) override
{
QAction* action = windowAdvisor->mapPerspIdToAction[perspective->GetId()];
if (action)
{
action->setChecked(true);
}
}
void PerspectiveDeactivated(const berry::IWorkbenchPage::Pointer& /*page*/,
const berry::IPerspectiveDescriptor::Pointer& perspective) override
{
QAction* action = windowAdvisor->mapPerspIdToAction[perspective->GetId()];
if (action)
{
action->setChecked(false);
}
}
private:
QmitkFlowApplicationWorkbenchWindowAdvisor* windowAdvisor;
};
QmitkFlowApplicationWorkbenchWindowAdvisor::QmitkFlowApplicationWorkbenchWindowAdvisor(berry::WorkbenchAdvisor* wbAdvisor,
berry::IWorkbenchWindowConfigurer::Pointer configurer)
: berry::WorkbenchWindowAdvisor(configurer)
, lastInput(nullptr)
, wbAdvisor(wbAdvisor)
, showViewToolbar(true)
, showVersionInfo(true)
, showMitkVersionInfo(true)
, showMemoryIndicator(true)
, dropTargetListener(new QmitkDefaultDropTargetListener)
{
productName = QCoreApplication::applicationName();
viewExcludeList.push_back("org.mitk.views.viewnavigator");
}
QmitkFlowApplicationWorkbenchWindowAdvisor::~QmitkFlowApplicationWorkbenchWindowAdvisor()
{
}
QWidget* QmitkFlowApplicationWorkbenchWindowAdvisor::CreateEmptyWindowContents(QWidget* parent)
{
QWidget* parentWidget = static_cast<QWidget*>(parent);
auto label = new QLabel(parentWidget);
label->setText("<b>No perspectives are open. Open a perspective in the <i>Window->Open Perspective</i> menu.</b>");
label->setContentsMargins(10,10,10,10);
label->setAlignment(Qt::AlignTop);
label->setEnabled(false);
parentWidget->layout()->addWidget(label);
return label;
}
void QmitkFlowApplicationWorkbenchWindowAdvisor::ShowMemoryIndicator(bool show)
{
showMemoryIndicator = show;
}
bool QmitkFlowApplicationWorkbenchWindowAdvisor::GetShowMemoryIndicator()
{
return showMemoryIndicator;
}
void QmitkFlowApplicationWorkbenchWindowAdvisor::ShowViewToolbar(bool show)
{
showViewToolbar = show;
}
void QmitkFlowApplicationWorkbenchWindowAdvisor::ShowVersionInfo(bool show)
{
showVersionInfo = show;
}
void QmitkFlowApplicationWorkbenchWindowAdvisor::ShowMitkVersionInfo(bool show)
{
showMitkVersionInfo = show;
}
void QmitkFlowApplicationWorkbenchWindowAdvisor::SetProductName(const QString& product)
{
productName = product;
}
void QmitkFlowApplicationWorkbenchWindowAdvisor::SetWindowIcon(const QString& wndIcon)
{
windowIcon = wndIcon;
}
void QmitkFlowApplicationWorkbenchWindowAdvisor::PostWindowCreate()
{
// very bad hack...
berry::IWorkbenchWindow::Pointer window = this->GetWindowConfigurer()->GetWindow();
QMainWindow* mainWindow = qobject_cast<QMainWindow*> (window->GetShell()->GetControl());
if (!windowIcon.isEmpty())
{
mainWindow->setWindowIcon(QIcon(windowIcon));
}
mainWindow->setContextMenuPolicy(Qt::PreventContextMenu);
// Load icon theme
QIcon::setThemeSearchPaths(QStringList() << QStringLiteral(":/org_mitk_icons/icons/"));
QIcon::setThemeName(QStringLiteral("awesome"));
// ==== Application menu ============================
QMenuBar* menuBar = mainWindow->menuBar();
menuBar->setContextMenuPolicy(Qt::PreventContextMenu);
#ifdef __APPLE__
menuBar->setNativeMenuBar(true);
#else
menuBar->setNativeMenuBar(false);
#endif
auto basePath = QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/");
fileSaveProjectAction = new QmitkExtFileSaveProjectAction(window);
fileSaveProjectAction->setIcon(berry::QtStyleManager::ThemeIcon(basePath + "document-save.svg"));
auto perspGroup = new QActionGroup(menuBar);
std::map<QString, berry::IViewDescriptor::Pointer> VDMap;
// sort elements (converting vector to map...)
QList<berry::IViewDescriptor::Pointer>::const_iterator iter;
berry::IViewRegistry* viewRegistry =
berry::PlatformUI::GetWorkbench()->GetViewRegistry();
const QList<berry::IViewDescriptor::Pointer> viewDescriptors = viewRegistry->GetViews();
bool skip = false;
for (iter = viewDescriptors.begin(); iter != viewDescriptors.end(); ++iter)
{
// if viewExcludeList is set, it contains the id-strings of view, which
// should not appear as an menu-entry in the menu
if (viewExcludeList.size() > 0)
{
for (int i=0; i<viewExcludeList.size(); i++)
{
if (viewExcludeList.at(i) == (*iter)->GetId())
{
skip = true;
break;
}
}
if (skip)
{
skip = false;
continue;
}
}
if ((*iter)->GetId() == "org.blueberry.ui.internal.introview")
continue;
if ((*iter)->GetId() == "org.mitk.views.imagenavigator")
continue;
if ((*iter)->GetId() == "org.mitk.views.viewnavigator")
continue;
std::pair<QString, berry::IViewDescriptor::Pointer> p((*iter)->GetLabel(), (*iter));
VDMap.insert(p);
}
std::map<QString, berry::IViewDescriptor::Pointer>::const_iterator MapIter;
for (MapIter = VDMap.begin(); MapIter != VDMap.end(); ++MapIter)
{
berry::QtShowViewAction* viewAction = new berry::QtShowViewAction(window, (*MapIter).second);
viewActions.push_back(viewAction);
}
QMenu* fileMenu = menuBar->addMenu("&File");
fileMenu->setObjectName("FileMenu");
fileMenu->addAction(fileSaveProjectAction);
fileMenu->addSeparator();
QAction* fileExitAction = new QmitkFileExitAction(window);
fileExitAction->setIcon(berry::QtStyleManager::ThemeIcon(basePath + "system-log-out.svg"));
fileExitAction->setShortcut(QKeySequence::Quit);
fileExitAction->setObjectName("QmitkFileExitAction");
fileMenu->addAction(fileExitAction);
// another bad hack to get an edit/undo menu...
QMenu* editMenu = menuBar->addMenu("&Edit");
undoAction = editMenu->addAction(berry::QtStyleManager::ThemeIcon(basePath + "edit-undo.svg"),
"&Undo",
QmitkFlowApplicationWorkbenchWindowAdvisorHack::undohack, SLOT(onUndo()),
QKeySequence("CTRL+Z"));
undoAction->setToolTip("Undo the last action (not supported by all modules)");
redoAction = editMenu->addAction(berry::QtStyleManager::ThemeIcon(basePath + "edit-redo.svg"),
"&Redo",
QmitkFlowApplicationWorkbenchWindowAdvisorHack::undohack, SLOT(onRedo()),
QKeySequence("CTRL+Y"));
redoAction->setToolTip("execute the last action that was undone again (not supported by all modules)");
// ==== Window Menu ==========================
QMenu* windowMenu = menuBar->addMenu("Window");
QMenu* perspMenu = windowMenu->addMenu("&Open Perspective");
windowMenu->addSeparator();
resetPerspAction = windowMenu->addAction("&Reset Perspective",
QmitkFlowApplicationWorkbenchWindowAdvisorHack::undohack, SLOT(onResetPerspective()));
windowMenu->addSeparator();
windowMenu->addAction("&Preferences...",
QmitkFlowApplicationWorkbenchWindowAdvisorHack::undohack, SLOT(onEditPreferences()),
QKeySequence("CTRL+P"));
// fill perspective menu
berry::IPerspectiveRegistry* perspRegistry =
window->GetWorkbench()->GetPerspectiveRegistry();
QList<berry::IPerspectiveDescriptor::Pointer> perspectives(
perspRegistry->GetPerspectives());
skip = false;
for (QList<berry::IPerspectiveDescriptor::Pointer>::iterator perspIt =
perspectives.begin(); perspIt != perspectives.end(); ++perspIt)
{
// if perspectiveExcludeList is set, it contains the id-strings of perspectives, which
// should not appear as an menu-entry in the perspective menu
if (perspectiveExcludeList.size() > 0)
{
for (int i=0; i<perspectiveExcludeList.size(); i++)
{
if (perspectiveExcludeList.at(i) == (*perspIt)->GetId())
{
skip = true;
break;
}
}
if (skip)
{
skip = false;
continue;
}
}
QAction* perspAction = new berry::QtOpenPerspectiveAction(window, *perspIt, perspGroup);
mapPerspIdToAction.insert((*perspIt)->GetId(), perspAction);
}
perspMenu->addActions(perspGroup->actions());
// ===== Help menu ====================================
QMenu* helpMenu = menuBar->addMenu("&Help");
helpMenu->addAction("&Welcome",this, SLOT(onIntro()));
helpMenu->addAction("&Open Help Perspective", this, SLOT(onHelpOpenHelpPerspective()));
helpMenu->addAction("&Context Help",this, SLOT(onHelp()), QKeySequence("F1"));
helpMenu->addAction("&About",this, SLOT(onAbout()));
// =====================================================
// toolbar for showing file open, undo, redo and other main actions
auto mainActionsToolBar = new QToolBar;
mainActionsToolBar->setObjectName("mainActionsToolBar");
mainActionsToolBar->setContextMenuPolicy(Qt::PreventContextMenu);
#ifdef __APPLE__
mainActionsToolBar->setToolButtonStyle ( Qt::ToolButtonTextUnderIcon );
#else
mainActionsToolBar->setToolButtonStyle ( Qt::ToolButtonTextBesideIcon );
#endif
basePath = QStringLiteral(":/org.mitk.gui.qt.ext/");
imageNavigatorAction = new QAction(berry::QtStyleManager::ThemeIcon(basePath + "image_navigator.svg"), "&Image Navigator", nullptr);
bool imageNavigatorViewFound = window->GetWorkbench()->GetViewRegistry()->Find("org.mitk.views.imagenavigator");
if (imageNavigatorViewFound)
{
QObject::connect(imageNavigatorAction, SIGNAL(triggered(bool)), QmitkFlowApplicationWorkbenchWindowAdvisorHack::undohack, SLOT(onImageNavigator()));
imageNavigatorAction->setCheckable(true);
// add part listener for image navigator
imageNavigatorPartListener.reset(new PartListenerForImageNavigator(imageNavigatorAction));
window->GetPartService()->AddPartListener(imageNavigatorPartListener.data());
berry::IViewPart::Pointer imageNavigatorView = window->GetActivePage()->FindView("org.mitk.views.imagenavigator");
imageNavigatorAction->setChecked(false);
if (imageNavigatorView)
{
bool isImageNavigatorVisible = window->GetActivePage()->IsPartVisible(imageNavigatorView);
if (isImageNavigatorVisible)
imageNavigatorAction->setChecked(true);
}
imageNavigatorAction->setToolTip("Toggle image navigator for navigating through image");
}
mainActionsToolBar->addAction(undoAction);
mainActionsToolBar->addAction(redoAction);
if (imageNavigatorViewFound)
{
mainActionsToolBar->addAction(imageNavigatorAction);
}
mainWindow->addToolBar(mainActionsToolBar);
// ==== View Toolbar ==================================
if (showViewToolbar)
{
auto prefService = berry::WorkbenchPlugin::GetDefault()->GetPreferencesService();
berry::IPreferences::Pointer stylePrefs = prefService->GetSystemPreferences()->Node(berry::QtPreferences::QT_STYLES_NODE);
bool showCategoryNames = stylePrefs->GetBool(berry::QtPreferences::QT_SHOW_TOOLBAR_CATEGORY_NAMES, true);
// Order view descriptors by category
QMultiMap<QString, berry::IViewDescriptor::Pointer> categoryViewDescriptorMap;
for (auto labelViewDescriptorPair : VDMap)
{
auto viewDescriptor = labelViewDescriptorPair.second;
auto category = !viewDescriptor->GetCategoryPath().isEmpty()
? viewDescriptor->GetCategoryPath().back()
: QString();
categoryViewDescriptorMap.insert(category, viewDescriptor);
}
// Create a separate toolbar for each category
for (auto category : categoryViewDescriptorMap.uniqueKeys())
{
auto viewDescriptorsInCurrentCategory = categoryViewDescriptorMap.values(category);
QList<berry::SmartPointer<berry::IViewDescriptor> > relevantViewDescriptors;
for (auto viewDescriptor : viewDescriptorsInCurrentCategory)
{
if (viewDescriptor->GetId() != "org.mitk.views.flow.control")
{
relevantViewDescriptors.push_back(viewDescriptor);
}
}
if (!relevantViewDescriptors.isEmpty())
{
auto toolbar = new QToolBar;
toolbar->setObjectName(category + " View Toolbar");
mainWindow->addToolBar(toolbar);
if (showCategoryNames && !category.isEmpty())
{
auto categoryButton = new QToolButton;
categoryButton->setToolButtonStyle(Qt::ToolButtonTextOnly);
categoryButton->setText(category);
categoryButton->setStyleSheet("background: transparent; margin: 0; padding: 0;");
toolbar->addWidget(categoryButton);
connect(categoryButton, &QToolButton::clicked, [toolbar]()
{
for (QWidget* widget : toolbar->findChildren<QWidget*>())
{
if (QStringLiteral("qt_toolbar_ext_button") == widget->objectName() && widget->isVisible())
{
QMouseEvent pressEvent(QEvent::MouseButtonPress, QPointF(0.0f, 0.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPointF(0.0f, 0.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QApplication::sendEvent(widget, &pressEvent);
QApplication::sendEvent(widget, &releaseEvent);
}
}
});
}
for (auto viewDescriptor : relevantViewDescriptors)
{
auto viewAction = new berry::QtShowViewAction(window, viewDescriptor);
toolbar->addAction(viewAction);
}
}
}
}
QSettings settings(GetQSettingsFile(), QSettings::IniFormat);
mainWindow->restoreState(settings.value("ToolbarPosition").toByteArray());
auto qStatusBar = new QStatusBar();
//creating a QmitkStatusBar for Output on the QStatusBar and connecting it with the MainStatusBar
auto statusBar = new QmitkStatusBar(qStatusBar);
//disabling the SizeGrip in the lower right corner
statusBar->SetSizeGripEnabled(false);
auto progBar = new QmitkProgressBar();
qStatusBar->addPermanentWidget(progBar, 0);
progBar->hide();
mainWindow->setStatusBar(qStatusBar);
if (showMemoryIndicator)
{
auto memoryIndicator = new QmitkMemoryUsageIndicatorView();
qStatusBar->addPermanentWidget(memoryIndicator, 0);
}
}
void QmitkFlowApplicationWorkbenchWindowAdvisor::PreWindowOpen()
{
berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer();
this->HookTitleUpdateListeners(configurer);
menuPerspectiveListener.reset(new PerspectiveListenerForMenu(this));
configurer->GetWindow()->AddPerspectiveListener(menuPerspectiveListener.data());
configurer->AddEditorAreaTransfer(QStringList("text/uri-list"));
configurer->ConfigureEditorAreaDropListener(dropTargetListener.data());
}
void QmitkFlowApplicationWorkbenchWindowAdvisor::PostWindowOpen()
{
berry::WorkbenchWindowAdvisor::PostWindowOpen();
// Force Rendering Window Creation on startup.
berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer();
ctkPluginContext* context = QmitkFlowApplicationPlugin::GetDefault()->GetPluginContext();
ctkServiceReference serviceRef = context->getServiceReference<mitk::IDataStorageService>();
if (serviceRef)
{
mitk::IDataStorageService *dsService = context->getService<mitk::IDataStorageService>(serviceRef);
if (dsService)
{
mitk::IDataStorageReference::Pointer dsRef = dsService->GetDataStorage();
mitk::DataStorageEditorInput::Pointer dsInput(new mitk::DataStorageEditorInput(dsRef));
mitk::WorkbenchUtil::OpenEditor(configurer->GetWindow()->GetActivePage(),dsInput);
}
}
}
void QmitkFlowApplicationWorkbenchWindowAdvisor::onIntro()
{
QmitkFlowApplicationWorkbenchWindowAdvisorHack::undohack->onIntro();
}
void QmitkFlowApplicationWorkbenchWindowAdvisor::onHelp()
{
QmitkFlowApplicationWorkbenchWindowAdvisorHack::undohack->onHelp();
}
void QmitkFlowApplicationWorkbenchWindowAdvisor::onHelpOpenHelpPerspective()
{
QmitkFlowApplicationWorkbenchWindowAdvisorHack::undohack->onHelpOpenHelpPerspective();
}
void QmitkFlowApplicationWorkbenchWindowAdvisor::onAbout()
{
QmitkFlowApplicationWorkbenchWindowAdvisorHack::undohack->onAbout();
}
void QmitkFlowApplicationWorkbenchWindowAdvisor::HookTitleUpdateListeners(berry::IWorkbenchWindowConfigurer::Pointer configurer)
{
// hook up the listeners to update the window title
titlePartListener.reset(new PartListenerForTitle(this));
titlePerspectiveListener.reset(new PerspectiveListenerForTitle(this));
editorPropertyListener.reset(new berry::PropertyChangeIntAdapter<
QmitkFlowApplicationWorkbenchWindowAdvisor>(this,
&QmitkFlowApplicationWorkbenchWindowAdvisor::PropertyChange));
configurer->GetWindow()->AddPerspectiveListener(titlePerspectiveListener.data());
configurer->GetWindow()->GetPartService()->AddPartListener(titlePartListener.data());
}
QString QmitkFlowApplicationWorkbenchWindowAdvisor::ComputeTitle()
{
berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer();
berry::IWorkbenchPage::Pointer currentPage = configurer->GetWindow()->GetActivePage();
berry::IEditorPart::Pointer activeEditor;
if (currentPage)
{
activeEditor = lastActiveEditor.Lock();
}
QString title;
berry::IProduct::Pointer product = berry::Platform::GetProduct();
if (product.IsNotNull())
{
title = product->GetName();
}
if (title.isEmpty())
{
// instead of the product name, we use a custom variable for now
title = productName;
}
if(showMitkVersionInfo)
{
QString mitkVersionInfo = MITK_REVISION_DESC;
if(mitkVersionInfo.isEmpty())
mitkVersionInfo = MITK_VERSION_STRING;
title += " " + mitkVersionInfo;
}
if (showVersionInfo)
{
// add version informatioin
QString versions = QString(" (ITK %1.%2.%3 | VTK %4.%5.%6 | Qt %7)")
.arg(ITK_VERSION_MAJOR).arg(ITK_VERSION_MINOR).arg(ITK_VERSION_PATCH)
.arg(VTK_MAJOR_VERSION).arg(VTK_MINOR_VERSION).arg(VTK_BUILD_VERSION)
.arg(QT_VERSION_STR);
title += versions;
}
if (currentPage)
{
if (activeEditor)
{
lastEditorTitle = activeEditor->GetTitleToolTip();
if (!lastEditorTitle.isEmpty())
title = lastEditorTitle + " - " + title;
}
berry::IPerspectiveDescriptor::Pointer persp = currentPage->GetPerspective();
QString label = "";
if (persp)
{
label = persp->GetLabel();
}
berry::IAdaptable* input = currentPage->GetInput();
if (input && input != wbAdvisor->GetDefaultPageInput())
{
label = currentPage->GetLabel();
}
if (!label.isEmpty())
{
title = label + " - " + title;
}
}
title += " (Not for use in diagnosis or treatment of patients)";
return title;
}
void QmitkFlowApplicationWorkbenchWindowAdvisor::RecomputeTitle()
{
berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer();
QString oldTitle = configurer->GetTitle();
QString newTitle = ComputeTitle();
if (newTitle != oldTitle)
{
configurer->SetTitle(newTitle);
}
}
void QmitkFlowApplicationWorkbenchWindowAdvisor::UpdateTitle(bool editorHidden)
{
berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer();
berry::IWorkbenchWindow::Pointer window = configurer->GetWindow();
berry::IEditorPart::Pointer activeEditor;
berry::IWorkbenchPage::Pointer currentPage = window->GetActivePage();
berry::IPerspectiveDescriptor::Pointer persp;
berry::IAdaptable* input = nullptr;
if (currentPage)
{
activeEditor = currentPage->GetActiveEditor();
persp = currentPage->GetPerspective();
input = currentPage->GetInput();
}
if (editorHidden)
{
activeEditor = nullptr;
}
// Nothing to do if the editor hasn't changed
if (activeEditor == lastActiveEditor.Lock() && currentPage == lastActivePage.Lock()
&& persp == lastPerspective.Lock() && input == lastInput)
{
return;
}
if (!lastActiveEditor.Expired())
{
lastActiveEditor.Lock()->RemovePropertyListener(editorPropertyListener.data());
}
lastActiveEditor = activeEditor;
lastActivePage = currentPage;
lastPerspective = persp;
lastInput = input;
if (activeEditor)
{
activeEditor->AddPropertyListener(editorPropertyListener.data());
}
RecomputeTitle();
}
void QmitkFlowApplicationWorkbenchWindowAdvisor::PropertyChange(const berry::Object::Pointer& /*source*/, int propId)
{
if (propId == berry::IWorkbenchPartConstants::PROP_TITLE)
{
if (!lastActiveEditor.Expired())
{
QString newTitle = lastActiveEditor.Lock()->GetPartName();
if (lastEditorTitle != newTitle)
{
RecomputeTitle();
}
}
}
}
void QmitkFlowApplicationWorkbenchWindowAdvisor::SetPerspectiveExcludeList(const QList<QString>& v)
{
this->perspectiveExcludeList = v;
}
QList<QString> QmitkFlowApplicationWorkbenchWindowAdvisor::GetPerspectiveExcludeList()
{
return this->perspectiveExcludeList;
}
void QmitkFlowApplicationWorkbenchWindowAdvisor::SetViewExcludeList(const QList<QString>& v)
{
this->viewExcludeList = v;
}
QList<QString> QmitkFlowApplicationWorkbenchWindowAdvisor::GetViewExcludeList()
{
return this->viewExcludeList;
}
void QmitkFlowApplicationWorkbenchWindowAdvisor::PostWindowClose()
{
berry::IWorkbenchWindow::Pointer window = this->GetWindowConfigurer()->GetWindow();
QMainWindow* mainWindow = static_cast<QMainWindow*> (window->GetShell()->GetControl());
QSettings settings(GetQSettingsFile(), QSettings::IniFormat);
settings.setValue("ToolbarPosition", mainWindow->saveState());
}
QString QmitkFlowApplicationWorkbenchWindowAdvisor::GetQSettingsFile() const
{
QFileInfo settingsInfo = QmitkFlowApplicationPlugin::GetDefault()->GetPluginContext()->getDataFile(QT_SETTINGS_FILENAME);
return settingsInfo.canonicalFilePath();
}
//--------------------------------------------------------------------------------
// Ugly hack from here on. Feel free to delete when command framework
// and undo buttons are done.
//--------------------------------------------------------------------------------
QmitkFlowApplicationWorkbenchWindowAdvisorHack::QmitkFlowApplicationWorkbenchWindowAdvisorHack()
: QObject()
{
}
QmitkFlowApplicationWorkbenchWindowAdvisorHack::~QmitkFlowApplicationWorkbenchWindowAdvisorHack()
{
}
void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onUndo()
{
mitk::UndoModel* model = mitk::UndoController::GetCurrentUndoModel();
if (model)
{
if (mitk::VerboseLimitedLinearUndo* verboseundo = dynamic_cast<mitk::VerboseLimitedLinearUndo*>(model))
{
mitk::VerboseLimitedLinearUndo::StackDescription descriptions = verboseundo->GetUndoDescriptions();
if (descriptions.size() >= 1)
{
MITK_INFO << "Undo " << descriptions.front().second;
}
}
model->Undo();
}
else
{
MITK_ERROR << "No undo model instantiated";
}
}
void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onRedo()
{
mitk::UndoModel* model = mitk::UndoController::GetCurrentUndoModel();
if (model)
{
if (mitk::VerboseLimitedLinearUndo* verboseundo = dynamic_cast<mitk::VerboseLimitedLinearUndo*>(model))
{
mitk::VerboseLimitedLinearUndo::StackDescription descriptions = verboseundo->GetRedoDescriptions();
if (descriptions.size() >= 1)
{
MITK_INFO << "Redo " << descriptions.front().second;
}
}
model->Redo();
}
else
{
MITK_ERROR << "No undo model instantiated";
}
}
// safe calls to the complete chain
// berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->FindView("org.mitk.views.imagenavigator");
// to cover for all possible cases of closed pages etc.
static void SafeHandleNavigatorView(QString view_query_name)
{
berry::IWorkbench* wbench = berry::PlatformUI::GetWorkbench();
if (wbench == nullptr)
return;
berry::IWorkbenchWindow::Pointer wbench_window = wbench->GetActiveWorkbenchWindow();
if (wbench_window.IsNull())
return;
berry::IWorkbenchPage::Pointer wbench_page = wbench_window->GetActivePage();
if (wbench_page.IsNull())
return;
auto wbench_view = wbench_page->FindView(view_query_name);
if (wbench_view.IsNotNull())
{
bool isViewVisible = wbench_page->IsPartVisible(wbench_view);
if (isViewVisible)
{
wbench_page->HideView(wbench_view);
return;
}
}
wbench_page->ShowView(view_query_name);
}
void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onImageNavigator()
{
// show/hide ImageNavigatorView
SafeHandleNavigatorView("org.mitk.views.imagenavigator");
}
void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onEditPreferences()
{
QmitkPreferencesDialog _PreferencesDialog(QApplication::activeWindow());
_PreferencesDialog.exec();
}
void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onQuit()
{
berry::PlatformUI::GetWorkbench()->Close();
}
void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onResetPerspective()
{
berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->ResetPerspective();
}
void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onClosePerspective()
{
berry::IWorkbenchPage::Pointer page =
berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage();
page->ClosePerspective(page->GetPerspective(), true, true);
}
void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onIntro()
{
bool hasIntro =
berry::PlatformUI::GetWorkbench()->GetIntroManager()->HasIntro();
if (!hasIntro)
{
QRegExp reg("(.*)<title>(\\n)*");
QRegExp reg2("(\\n)*</title>(.*)");
QFile file(":/org.mitk.gui.qt.ext/index.html");
file.open(QIODevice::ReadOnly | QIODevice::Text); //text file only for reading
QString text = QString(file.readAll());
file.close();
QString title = text;
title.replace(reg, "");
title.replace(reg2, "");
std::cout << title.toStdString() << std::endl;
QMessageBox::information(nullptr, title,
text, "Close");
}
else
{
berry::PlatformUI::GetWorkbench()->GetIntroManager()->ShowIntro(
berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow(), false);
}
}
void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onHelp()
{
ctkPluginContext* context = QmitkFlowApplicationPlugin::GetDefault()->GetPluginContext();
if (context == nullptr)
{
MITK_WARN << "Plugin context not set, unable to open context help";
return;
}
// Check if the org.blueberry.ui.qt.help plug-in is installed and started
QList<QSharedPointer<ctkPlugin> > plugins = context->getPlugins();
foreach(QSharedPointer<ctkPlugin> p, plugins)
{
if (p->getSymbolicName() == "org.blueberry.ui.qt.help")
{
if (p->getState() != ctkPlugin::ACTIVE)
{
// try to activate the plug-in explicitly
try
{
p->start(ctkPlugin::START_TRANSIENT);
}
catch (const ctkPluginException& pe)
{
MITK_ERROR << "Activating org.blueberry.ui.qt.help failed: " << pe.what();
return;
}
}
}
}
ctkServiceReference eventAdminRef = context->getServiceReference<ctkEventAdmin>();
ctkEventAdmin* eventAdmin = nullptr;
if (eventAdminRef)
{
eventAdmin = context->getService<ctkEventAdmin>(eventAdminRef);
}
if (eventAdmin == nullptr)
{
MITK_WARN << "ctkEventAdmin service not found. Unable to open context help";
}
else
{
ctkEvent ev("org/blueberry/ui/help/CONTEXTHELP_REQUESTED");
eventAdmin->postEvent(ev);
}
}
void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onHelpOpenHelpPerspective()
{
berry::PlatformUI::GetWorkbench()->ShowPerspective("org.blueberry.perspectives.help",
berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow());
}
void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onAbout()
{
auto aboutDialog = new QmitkAboutDialog(QApplication::activeWindow(), nullptr);
aboutDialog->open();
}
diff --git a/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/CMakeLists.txt b/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/CMakeLists.txt
index b5c7ec2ed4..f7c18446e3 100644
--- a/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/CMakeLists.txt
+++ b/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/CMakeLists.txt
@@ -1,16 +1,16 @@
project(org_mitk_gui_qt_igt_app_ultrasoundtrackingnavigation)
mitk_create_plugin(
EXPORT_DIRECTIVE IGTAPPUSTRACKINGNAVIGATION_EXPORT
EXPORTED_INCLUDE_SUFFIXES src
- PACKAGE_DEPENDS PRIVATE CTK ITK|LabelMap Poco
+ PACKAGE_DEPENDS PRIVATE CTK ITK|LabelMap+Smoothing Poco
MODULE_DEPENDS MitkUSUI MitkUSNavigation MitkIGTUI MitkSceneSerialization MitkContourModel
)
#usFunctionAddResources(TARGET ${PLUGIN_TARGET}
# MODULE_NAME liborg_mitk_gui_qt_usnavigation
# WORKING_DIRECTORY resources
# FILES Interactions/USPointMarkInteractions.xml
# Interactions/USZoneInteractions.xml
# Interactions/USZoneInteractionsHold.xml
#)
diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp
index eaed8a6bcc..4fb59d9e64 100644
--- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp
+++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp
@@ -1,503 +1,503 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "QmitkImageCropperView.h"
#include <mitkBoundingShapeCropper.h>
#include <mitkDisplayInteractor.h>
#include <mitkImageStatisticsHolder.h>
#include <mitkInteractionConst.h>
#include <mitkITKImageImport.h>
#include <mitkLabelSetImage.h>
#include <mitkNodePredicateDataType.h>
#include <mitkNodePredicateAnd.h>
#include <mitkNodePredicateNot.h>
#include <mitkNodePredicateProperty.h>
#include <mitkNodePredicateFunction.h>
#include <mitkRenderingManager.h>
#include <usModuleRegistry.h>
#include <QMessageBox>
const std::string QmitkImageCropperView::VIEW_ID = "org.mitk.views.qmitkimagecropper";
QmitkImageCropperView::QmitkImageCropperView(QObject *)
: m_ParentWidget(nullptr)
, m_BoundingShapeInteractor(nullptr)
, m_CropOutsideValue(0)
{
CreateBoundingShapeInteractor(false);
}
QmitkImageCropperView::~QmitkImageCropperView()
{
//disable interactor
if (m_BoundingShapeInteractor != nullptr)
{
m_BoundingShapeInteractor->SetDataNode(nullptr);
m_BoundingShapeInteractor->EnableInteraction(false);
}
}
void QmitkImageCropperView::CreateQtPartControl(QWidget *parent)
{
// create GUI widgets from the Qt Designer's .ui file
m_Controls.setupUi(parent);
m_Controls.imageSelectionWidget->SetDataStorage(GetDataStorage());
m_Controls.imageSelectionWidget->SetNodePredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")));
m_Controls.imageSelectionWidget->SetSelectionIsOptional(true);
m_Controls.imageSelectionWidget->SetAutoSelectNewNodes(true);
m_Controls.imageSelectionWidget->SetEmptyInfo(QString("Please select an image node"));
m_Controls.imageSelectionWidget->SetPopUpTitel(QString("Select image node"));
connect(m_Controls.imageSelectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged,
this, &QmitkImageCropperView::OnImageSelectionChanged);
m_Controls.boundingBoxSelectionWidget->SetDataStorage(GetDataStorage());
m_Controls.boundingBoxSelectionWidget->SetNodePredicate(mitk::NodePredicateAnd::New(
mitk::TNodePredicateDataType<mitk::GeometryData>::New(),
mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))));
m_Controls.boundingBoxSelectionWidget->SetSelectionIsOptional(true);
m_Controls.boundingBoxSelectionWidget->SetAutoSelectNewNodes(true);
m_Controls.boundingBoxSelectionWidget->SetEmptyInfo(QString("Please select a bounding box"));
m_Controls.boundingBoxSelectionWidget->SetPopUpTitel(QString("Select bounding box node"));
connect(m_Controls.boundingBoxSelectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged,
this, &QmitkImageCropperView::OnBoundingBoxSelectionChanged);
connect(m_Controls.buttonCreateNewBoundingBox, SIGNAL(clicked()), this, SLOT(OnCreateNewBoundingBox()));
connect(m_Controls.buttonCropping, SIGNAL(clicked()), this, SLOT(OnCropping()));
connect(m_Controls.buttonMasking, SIGNAL(clicked()), this, SLOT(OnMasking()));
auto lambda = [this]()
{
m_Controls.groupImageSettings->setVisible(!m_Controls.groupImageSettings->isVisible());
};
connect(m_Controls.buttonAdvancedSettings, &ctkExpandButton::clicked, this, lambda);
connect(m_Controls.spinBoxOutsidePixelValue, SIGNAL(valueChanged(int)), this, SLOT(OnSliderValueChanged(int)));
SetDefaultGUI();
m_ParentWidget = parent;
this->OnImageSelectionChanged(m_Controls.imageSelectionWidget->GetSelectedNodes());
this->OnBoundingBoxSelectionChanged(m_Controls.boundingBoxSelectionWidget->GetSelectedNodes());
}
void QmitkImageCropperView::OnImageSelectionChanged(QList<mitk::DataNode::Pointer>)
{
bool rotationEnabled = false;
auto imageNode = m_Controls.imageSelectionWidget->GetSelectedNode();
if (imageNode.IsNull())
{
SetDefaultGUI();
return;
}
auto image = dynamic_cast<mitk::Image*>(imageNode->GetData());
if (nullptr != image)
{
if (image->GetDimension() < 3)
{
QMessageBox::warning(nullptr,
tr("Invalid image selected"),
tr("ImageCropper only works with 3 or more dimensions."),
QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton);
SetDefaultGUI();
return;
}
m_ParentWidget->setEnabled(true);
m_Controls.buttonCreateNewBoundingBox->setEnabled(true);
vtkSmartPointer<vtkMatrix4x4> imageMat = image->GetGeometry()->GetVtkMatrix();
// check whether the image geometry is rotated; if so, no pixel aligned cropping or masking can be performed
if ((imageMat->GetElement(1, 0) == 0.0) && (imageMat->GetElement(0, 1) == 0.0) &&
(imageMat->GetElement(1, 2) == 0.0) && (imageMat->GetElement(2, 1) == 0.0) &&
(imageMat->GetElement(2, 0) == 0.0) && (imageMat->GetElement(0, 2) == 0.0))
{
rotationEnabled = false;
m_Controls.labelWarningRotation->setVisible(false);
}
else
{
rotationEnabled = true;
m_Controls.labelWarningRotation->setStyleSheet(" QLabel { color: rgb(255, 0, 0) }");
m_Controls.labelWarningRotation->setVisible(true);
}
this->CreateBoundingShapeInteractor(rotationEnabled);
- if (itk::ImageIOBase::SCALAR == image->GetPixelType().GetPixelType())
+ if (itk::IOPixelEnum::SCALAR == image->GetPixelType().GetPixelType())
{
// Might be changed with the upcoming new image statistics plugin
//(recomputation might be very expensive for large images ;) )
auto statistics = image->GetStatistics();
auto minPixelValue = statistics->GetScalarValueMin();
auto maxPixelValue = statistics->GetScalarValueMax();
if (minPixelValue < std::numeric_limits<int>::min())
{
minPixelValue = std::numeric_limits<int>::min();
}
if (maxPixelValue > std::numeric_limits<int>::max())
{
maxPixelValue = std::numeric_limits<int>::max();
}
m_Controls.spinBoxOutsidePixelValue->setEnabled(true);
m_Controls.spinBoxOutsidePixelValue->setMaximum(static_cast<int>(maxPixelValue));
m_Controls.spinBoxOutsidePixelValue->setMinimum(static_cast<int>(minPixelValue));
m_Controls.spinBoxOutsidePixelValue->setValue(static_cast<int>(minPixelValue));
}
else
{
m_Controls.spinBoxOutsidePixelValue->setEnabled(false);
}
unsigned int dim = image->GetDimension();
if (dim < 2 || dim > 4)
{
m_ParentWidget->setEnabled(false);
}
if (m_Controls.boundingBoxSelectionWidget->GetSelectedNode().IsNotNull())
{
m_Controls.buttonCropping->setEnabled(true);
m_Controls.buttonMasking->setEnabled(true);
m_Controls.buttonAdvancedSettings->setEnabled(true);
m_Controls.groupImageSettings->setEnabled(true);
}
}
}
void QmitkImageCropperView::OnBoundingBoxSelectionChanged(QList<mitk::DataNode::Pointer>)
{
auto boundingBoxNode = m_Controls.boundingBoxSelectionWidget->GetSelectedNode();
if (boundingBoxNode.IsNull())
{
SetDefaultGUI();
m_BoundingShapeInteractor->EnableInteraction(false);
m_BoundingShapeInteractor->SetDataNode(nullptr);
if (m_Controls.imageSelectionWidget->GetSelectedNode().IsNotNull())
{
m_Controls.buttonCreateNewBoundingBox->setEnabled(true);
}
return;
}
auto boundingBox = dynamic_cast<mitk::GeometryData*>(boundingBoxNode->GetData());
if (nullptr != boundingBox)
{
// node newly selected
boundingBoxNode->SetVisibility(true);
m_BoundingShapeInteractor->EnableInteraction(true);
m_BoundingShapeInteractor->SetDataNode(boundingBoxNode);
mitk::RenderingManager::GetInstance()->InitializeViews();
if (m_Controls.imageSelectionWidget->GetSelectedNode().IsNotNull())
{
m_Controls.buttonCropping->setEnabled(true);
m_Controls.buttonMasking->setEnabled(true);
m_Controls.buttonAdvancedSettings->setEnabled(true);
m_Controls.groupImageSettings->setEnabled(true);
}
}
}
void QmitkImageCropperView::OnCreateNewBoundingBox()
{
auto imageNode = m_Controls.imageSelectionWidget->GetSelectedNode();
if (imageNode.IsNull())
{
return;
}
if (nullptr == imageNode->GetData())
{
return;
}
QString name = QString::fromStdString(imageNode->GetName() + " Bounding Shape");
auto boundingShape = this->GetDataStorage()->GetNode(mitk::NodePredicateFunction::New([&name](const mitk::DataNode *node)
{
return 0 == node->GetName().compare(name.toStdString());
}));
if (nullptr != boundingShape)
{
name = this->AdaptBoundingObjectName(name);
}
// get current timestep to support 3d+t images
auto renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN);
const auto timePoint = renderWindowPart->GetSelectedTimePoint();
const auto imageGeometry = imageNode->GetData()->GetTimeGeometry()->GetGeometryForTimePoint(timePoint);
auto boundingBox = mitk::GeometryData::New();
boundingBox->SetGeometry(static_cast<mitk::Geometry3D*>(this->InitializeWithImageGeometry(imageGeometry)));
auto boundingBoxNode = mitk::DataNode::New();
boundingBoxNode->SetData(boundingBox);
boundingBoxNode->SetProperty("name", mitk::StringProperty::New(name.toStdString()));
boundingBoxNode->SetProperty("color", mitk::ColorProperty::New(1.0, 1.0, 1.0));
boundingBoxNode->SetProperty("opacity", mitk::FloatProperty::New(0.6));
boundingBoxNode->SetProperty("layer", mitk::IntProperty::New(99));
boundingBoxNode->AddProperty("handle size factor", mitk::DoubleProperty::New(1.0 / 40.0));
boundingBoxNode->SetBoolProperty("pickable", true);
if (!this->GetDataStorage()->Exists(boundingBoxNode))
{
GetDataStorage()->Add(boundingBoxNode, imageNode);
}
m_Controls.boundingBoxSelectionWidget->SetCurrentSelectedNode(boundingBoxNode);
}
void QmitkImageCropperView::OnCropping()
{
this->ProcessImage(false);
}
void QmitkImageCropperView::OnMasking()
{
this->ProcessImage(true);
}
void QmitkImageCropperView::OnSliderValueChanged(int slidervalue)
{
m_CropOutsideValue = slidervalue;
}
void QmitkImageCropperView::CreateBoundingShapeInteractor(bool rotationEnabled)
{
if (m_BoundingShapeInteractor.IsNull())
{
m_BoundingShapeInteractor = mitk::BoundingShapeInteractor::New();
m_BoundingShapeInteractor->LoadStateMachine("BoundingShapeInteraction.xml", us::ModuleRegistry::GetModule("MitkBoundingShape"));
m_BoundingShapeInteractor->SetEventConfig("BoundingShapeMouseConfig.xml", us::ModuleRegistry::GetModule("MitkBoundingShape"));
}
m_BoundingShapeInteractor->SetRotationEnabled(rotationEnabled);
}
mitk::Geometry3D::Pointer QmitkImageCropperView::InitializeWithImageGeometry(const mitk::BaseGeometry* geometry) const
{
// convert a BaseGeometry into a Geometry3D (otherwise IO is not working properly)
if (geometry == nullptr)
mitkThrow() << "Geometry is not valid.";
auto boundingGeometry = mitk::Geometry3D::New();
boundingGeometry->SetBounds(geometry->GetBounds());
boundingGeometry->SetImageGeometry(geometry->GetImageGeometry());
boundingGeometry->SetOrigin(geometry->GetOrigin());
boundingGeometry->SetSpacing(geometry->GetSpacing());
boundingGeometry->SetIndexToWorldTransform(geometry->GetIndexToWorldTransform()->Clone());
boundingGeometry->Modified();
return boundingGeometry;
}
void QmitkImageCropperView::ProcessImage(bool mask)
{
auto renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN);
const auto timePoint = renderWindowPart->GetSelectedTimePoint();
auto imageNode = m_Controls.imageSelectionWidget->GetSelectedNode();
if (imageNode.IsNull())
{
QMessageBox::information(nullptr, "Warning", "Please load and select an image before starting image processing.");
return;
}
auto boundingBoxNode = m_Controls.boundingBoxSelectionWidget->GetSelectedNode();
if (boundingBoxNode.IsNull())
{
QMessageBox::information(nullptr, "Warning", "Please load and select a cropping object before starting image processing.");
return;
}
if (!imageNode->GetData()->GetTimeGeometry()->IsValidTimePoint(timePoint))
{
QMessageBox::information(nullptr, "Warning", "Please select a time point that is within the time bounds of the selected image.");
return;
}
const auto timeStep = imageNode->GetData()->GetTimeGeometry()->TimePointToTimeStep(timePoint);
auto image = dynamic_cast<mitk::Image*>(imageNode->GetData());
auto boundingBox = dynamic_cast<mitk::GeometryData*>(boundingBoxNode->GetData());
if (nullptr != image && nullptr != boundingBox)
{
QString imageName;
if (mask)
{
imageName = QString::fromStdString(imageNode->GetName() + "_" + boundingBoxNode->GetName() + "_masked");
}
else
{
imageName = QString::fromStdString(imageNode->GetName() + "_" + boundingBoxNode->GetName() + "_cropped");
}
if (m_Controls.checkBoxCropTimeStepOnly->isChecked())
{
imageName = imageName + "_T" + QString::number(timeStep);
}
// image and bounding shape ok, set as input
auto croppedImageNode = mitk::DataNode::New();
auto cutter = mitk::BoundingShapeCropper::New();
cutter->SetGeometry(boundingBox);
// adjustable in advanced settings
cutter->SetUseWholeInputRegion(mask); //either mask (mask=true) or crop (mask=false)
cutter->SetOutsideValue(m_CropOutsideValue);
cutter->SetUseCropTimeStepOnly(m_Controls.checkBoxCropTimeStepOnly->isChecked());
cutter->SetCurrentTimeStep(timeStep);
// TODO: Add support for MultiLayer (right now only Mulitlabel support)
auto labelsetImageInput = dynamic_cast<mitk::LabelSetImage*>(image);
if (nullptr != labelsetImageInput)
{
cutter->SetInput(labelsetImageInput);
// do the actual cutting
try
{
cutter->Update();
}
catch (const itk::ExceptionObject& e)
{
std::string message = std::string("The Cropping filter could not process because of: \n ") + e.GetDescription();
QMessageBox::warning(nullptr, tr("Cropping not possible!"), tr(message.c_str()),
QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton);
return;
}
auto labelSetImage = mitk::LabelSetImage::New();
labelSetImage->InitializeByLabeledImage(cutter->GetOutput());
for (unsigned int i = 0; i < labelsetImageInput->GetNumberOfLayers(); i++)
{
labelSetImage->AddLabelSetToLayer(i, labelsetImageInput->GetLabelSet(i));
}
croppedImageNode->SetData(labelSetImage);
croppedImageNode->SetProperty("name", mitk::StringProperty::New(imageName.toStdString()));
//add cropping result to the current data storage as child node to the image node
if (!m_Controls.checkOverwriteImage->isChecked())
{
if (!this->GetDataStorage()->Exists(croppedImageNode))
{
this->GetDataStorage()->Add(croppedImageNode, imageNode);
}
}
else // original image will be overwritten by the result image and the bounding box of the result is adjusted
{
imageNode->SetData(labelSetImage);
imageNode->Modified();
// Adjust coordinate system by doing a reinit on
auto tempDataStorage = mitk::DataStorage::SetOfObjects::New();
tempDataStorage->InsertElement(0, imageNode);
// initialize the views to the bounding geometry
auto bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(tempDataStorage);
mitk::RenderingManager::GetInstance()->InitializeViews(bounds);
}
}
else
{
cutter->SetInput(image);
// do the actual cutting
try
{
cutter->Update();
}
catch (const itk::ExceptionObject& e)
{
std::string message = std::string("The Cropping filter could not process because of: \n ") + e.GetDescription();
QMessageBox::warning(nullptr, tr("Cropping not possible!"), tr(message.c_str()),
QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton);
return;
}
//add cropping result to the current data storage as child node to the image node
if (!m_Controls.checkOverwriteImage->isChecked())
{
croppedImageNode->SetData(cutter->GetOutput());
croppedImageNode->SetProperty("name", mitk::StringProperty::New(imageName.toStdString()));
croppedImageNode->SetProperty("color", mitk::ColorProperty::New(1.0, 1.0, 1.0));
mitk::LevelWindow levelWindow;
imageNode->GetLevelWindow(levelWindow);
croppedImageNode->SetLevelWindow(levelWindow);
if (!this->GetDataStorage()->Exists(croppedImageNode))
{
this->GetDataStorage()->Add(croppedImageNode, imageNode);
imageNode->SetVisibility(mask); // Give the user a visual clue that something happened when image was cropped
}
}
else // original image will be overwritten by the result image and the bounding box of the result is adjusted
{
mitk::LevelWindow levelWindow;
imageNode->GetLevelWindow(levelWindow);
imageNode->SetData(cutter->GetOutput());
imageNode->SetLevelWindow(levelWindow);
// Adjust coordinate system by doing a reinit on
auto tempDataStorage = mitk::DataStorage::SetOfObjects::New();
tempDataStorage->InsertElement(0, imageNode);
// initialize the views to the bounding geometry
auto bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(tempDataStorage);
mitk::RenderingManager::GetInstance()->InitializeViews(bounds);
}
}
}
else
{
QMessageBox::information(nullptr, "Warning", "Please load and select an image before starting image processing.");
}
}
void QmitkImageCropperView::SetDefaultGUI()
{
m_Controls.labelWarningRotation->setVisible(false);
m_Controls.buttonCreateNewBoundingBox->setEnabled(false);
m_Controls.buttonCropping->setEnabled(false);
m_Controls.buttonMasking->setEnabled(false);
m_Controls.buttonAdvancedSettings->setEnabled(false);
m_Controls.groupImageSettings->setEnabled(false);
m_Controls.groupImageSettings->setVisible(false);
m_Controls.checkOverwriteImage->setChecked(false);
m_Controls.checkBoxCropTimeStepOnly->setChecked(false);
}
QString QmitkImageCropperView::AdaptBoundingObjectName(const QString& name) const
{
unsigned int counter = 2;
QString newName = QString("%1 %2").arg(name).arg(counter);
while (nullptr != this->GetDataStorage()->GetNode(mitk::NodePredicateFunction::New([&newName](const mitk::DataNode *node)
{
return 0 == node->GetName().compare(newName.toStdString());
})))
{
newName = QString("%1 %2").arg(name).arg(++counter);
}
return newName;
}
diff --git a/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp b/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp
index 76bc6fcd53..9aa912fb4a 100644
--- a/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp
+++ b/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp
@@ -1,612 +1,612 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "QmitkImageNavigatorView.h"
#include <itkSpatialOrientationAdapter.h>
#include <QmitkStepperAdapter.h>
#include <QmitkRenderWindow.h>
#include <mitkTimeGeometry.h>
#include <berryConstants.h>
#include <mitkPlaneGeometry.h>
#include <mitkNodePredicateDataType.h>
#include <mitkStatusBar.h>
#include <mitkPixelTypeMultiplex.h>
#include <mitkImagePixelReadAccessor.h>
#include <mitkNodePredicateProperty.h>
#include <mitkCompositePixelValueToString.h>
const std::string QmitkImageNavigatorView::VIEW_ID = "org.mitk.views.imagenavigator";
QmitkImageNavigatorView::QmitkImageNavigatorView()
: m_AxialStepper(nullptr)
, m_SagittalStepper(nullptr)
, m_FrontalStepper(nullptr)
, m_TimeStepper(nullptr)
, m_Parent(nullptr)
, m_IRenderWindowPart(nullptr)
{
}
QmitkImageNavigatorView::~QmitkImageNavigatorView()
{
}
void QmitkImageNavigatorView::CreateQtPartControl(QWidget *parent)
{
// create GUI widgets
m_Parent = parent;
m_Controls.setupUi(parent);
connect(m_Controls.m_XWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged()));
connect(m_Controls.m_YWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged()));
connect(m_Controls.m_ZWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged()));
m_Parent->setEnabled(false);
mitk::IRenderWindowPart* renderPart = this->GetRenderWindowPart();
this->RenderWindowPartActivated(renderPart);
}
void QmitkImageNavigatorView::SetFocus ()
{
m_Controls.m_XWorldCoordinateSpinBox->setFocus();
}
void QmitkImageNavigatorView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart)
{
if (this->m_IRenderWindowPart != renderWindowPart)
{
this->m_IRenderWindowPart = renderWindowPart;
this->m_Parent->setEnabled(true);
QmitkRenderWindow* renderWindow = renderWindowPart->GetQmitkRenderWindow("axial");
if (renderWindow)
{
if (m_AxialStepper) m_AxialStepper->deleteLater();
m_AxialStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorAxial,
renderWindow->GetSliceNavigationController()->GetSlice(),
"sliceNavigatorAxialFromSimpleExample");
m_Controls.m_SliceNavigatorAxial->setEnabled(true);
m_Controls.m_AxialLabel->setEnabled(true);
m_Controls.m_ZWorldCoordinateSpinBox->setEnabled(true);
connect(m_AxialStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch()));
connect(m_AxialStepper, SIGNAL(Refetch()), this, SLOT(UpdateStatusBar()));
}
else
{
m_Controls.m_SliceNavigatorAxial->setEnabled(false);
m_Controls.m_AxialLabel->setEnabled(false);
m_Controls.m_ZWorldCoordinateSpinBox->setEnabled(false);
}
renderWindow = renderWindowPart->GetQmitkRenderWindow("sagittal");
if (renderWindow)
{
if (m_SagittalStepper) m_SagittalStepper->deleteLater();
m_SagittalStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorSagittal,
renderWindow->GetSliceNavigationController()->GetSlice(),
"sliceNavigatorSagittalFromSimpleExample");
m_Controls.m_SliceNavigatorSagittal->setEnabled(true);
m_Controls.m_SagittalLabel->setEnabled(true);
m_Controls.m_YWorldCoordinateSpinBox->setEnabled(true);
connect(m_SagittalStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch()));
connect(m_SagittalStepper, SIGNAL(Refetch()), this, SLOT(UpdateStatusBar()));
}
else
{
m_Controls.m_SliceNavigatorSagittal->setEnabled(false);
m_Controls.m_SagittalLabel->setEnabled(false);
m_Controls.m_YWorldCoordinateSpinBox->setEnabled(false);
}
renderWindow = renderWindowPart->GetQmitkRenderWindow("coronal");
if (renderWindow)
{
if (m_FrontalStepper) m_FrontalStepper->deleteLater();
m_FrontalStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorFrontal,
renderWindow->GetSliceNavigationController()->GetSlice(),
"sliceNavigatorFrontalFromSimpleExample");
m_Controls.m_SliceNavigatorFrontal->setEnabled(true);
m_Controls.m_CoronalLabel->setEnabled(true);
m_Controls.m_XWorldCoordinateSpinBox->setEnabled(true);
connect(m_FrontalStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch()));
connect(m_FrontalStepper, SIGNAL(Refetch()), this, SLOT(UpdateStatusBar()));
}
else
{
m_Controls.m_SliceNavigatorFrontal->setEnabled(false);
m_Controls.m_CoronalLabel->setEnabled(false);
m_Controls.m_XWorldCoordinateSpinBox->setEnabled(false);
}
mitk::SliceNavigationController* timeController = renderWindowPart->GetTimeNavigationController();
if (timeController)
{
if (m_TimeStepper) m_TimeStepper->deleteLater();
m_TimeStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorTime,
timeController->GetTime(),
"sliceNavigatorTimeFromSimpleExample");
m_Controls.m_SliceNavigatorTime->setEnabled(true);
m_Controls.m_TimeLabel->setEnabled(true);
connect(m_TimeStepper, SIGNAL(Refetch()), this, SLOT(UpdateStatusBar()));
}
else
{
m_Controls.m_SliceNavigatorTime->setEnabled(false);
m_Controls.m_TimeLabel->setEnabled(false);
}
this->OnRefetch();
this->UpdateStatusBar();
}
}
void QmitkImageNavigatorView::UpdateStatusBar()
{
if (m_IRenderWindowPart != nullptr)
{
mitk::Point3D position = m_IRenderWindowPart->GetSelectedPosition();
mitk::BaseRenderer::Pointer baseRenderer = mitk::BaseRenderer::GetInstance(m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetVtkRenderWindow());
auto globalCurrentTimePoint = baseRenderer->GetTime();
mitk::TNodePredicateDataType<mitk::Image>::Pointer isImageData = mitk::TNodePredicateDataType<mitk::Image>::New();
mitk::DataStorage::SetOfObjects::ConstPointer nodes = GetDataStorage()->GetSubset(isImageData).GetPointer();
if (nodes.IsNotNull())
{
mitk::Image::Pointer image3D;
mitk::DataNode::Pointer node;
mitk::DataNode::Pointer topSourceNode;
int component = 0;
node = mitk::FindTopmostVisibleNode(nodes, position, globalCurrentTimePoint, baseRenderer);
if (node.IsNotNull())
{
bool isBinary(false);
node->GetBoolProperty("binary", isBinary);
if (isBinary)
{
mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = GetDataStorage()->GetSources(node, nullptr, true);
if (!sourcenodes->empty())
{
topSourceNode = mitk::FindTopmostVisibleNode(sourcenodes, position, globalCurrentTimePoint, baseRenderer);
}
if (topSourceNode.IsNotNull())
{
image3D = dynamic_cast<mitk::Image*>(topSourceNode->GetData());
topSourceNode->GetIntProperty("Image.Displayed Component", component);
}
else
{
image3D = dynamic_cast<mitk::Image*>(node->GetData());
node->GetIntProperty("Image.Displayed Component", component);
}
}
else
{
image3D = dynamic_cast<mitk::Image*>(node->GetData());
node->GetIntProperty("Image.Displayed Component", component);
}
}
// get the position and pixel value from the image and build up status bar text
auto statusBar = mitk::StatusBar::GetInstance();
if (image3D.IsNotNull() && statusBar != nullptr)
{
itk::Index<3> p;
image3D->GetGeometry()->WorldToIndex(position, p);
auto pixelType = image3D->GetChannelDescriptor().GetPixelType().GetPixelType();
- if (pixelType == itk::ImageIOBase::RGB || pixelType == itk::ImageIOBase::RGBA)
+ if (pixelType == itk::IOPixelEnum::RGB || pixelType == itk::IOPixelEnum::RGBA)
{
std::string pixelValue = "Pixel RGB(A) value: ";
pixelValue.append(ConvertCompositePixelValueToString(image3D, p));
statusBar->DisplayImageInfo(position, p, globalCurrentTimePoint, pixelValue.c_str());
}
- else if (pixelType == itk::ImageIOBase::DIFFUSIONTENSOR3D || pixelType == itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR)
+ else if (pixelType == itk::IOPixelEnum::DIFFUSIONTENSOR3D || pixelType == itk::IOPixelEnum::SYMMETRICSECONDRANKTENSOR)
{
std::string pixelValue = "See ODF Details view. ";
statusBar->DisplayImageInfo(position, p, globalCurrentTimePoint, pixelValue.c_str());
}
else
{
itk::Index<3> p;
image3D->GetGeometry()->WorldToIndex(position, p);
mitk::ScalarType pixelValue;
mitkPixelTypeMultiplex5(
mitk::FastSinglePixelAccess,
image3D->GetChannelDescriptor().GetPixelType(),
image3D,
image3D->GetVolumeData(image3D->GetTimeGeometry()->TimePointToTimeStep(globalCurrentTimePoint)),
p,
pixelValue,
component);
statusBar->DisplayImageInfo(position, p, globalCurrentTimePoint, pixelValue);
}
}
else
{
statusBar->DisplayImageInfoInvalid();
}
}
}
}
void QmitkImageNavigatorView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/)
{
m_IRenderWindowPart = nullptr;
m_Parent->setEnabled(false);
}
int QmitkImageNavigatorView::GetSizeFlags(bool width)
{
if(!width)
{
return berry::Constants::MIN | berry::Constants::MAX | berry::Constants::FILL;
}
else
{
return 0;
}
}
int QmitkImageNavigatorView::ComputePreferredSize(bool width, int /*availableParallel*/, int /*availablePerpendicular*/, int preferredResult)
{
if(width==false)
{
return 200;
}
else
{
return preferredResult;
}
}
int QmitkImageNavigatorView::GetClosestAxisIndex(mitk::Vector3D normal)
{
// cos(theta) = normal . axis
// cos(theta) = (a, b, c) . (d, e, f)
// cos(theta) = (a, b, c) . (1, 0, 0) = a
// cos(theta) = (a, b, c) . (0, 1, 0) = b
// cos(theta) = (a, b, c) . (0, 0, 1) = c
double absCosThetaWithAxis[3];
for (int i = 0; i < 3; i++)
{
absCosThetaWithAxis[i] = fabs(normal[i]);
}
int largestIndex = 0;
double largestValue = absCosThetaWithAxis[0];
for (int i = 1; i < 3; i++)
{
if (absCosThetaWithAxis[i] > largestValue)
{
largestValue = absCosThetaWithAxis[i];
largestIndex = i;
}
}
return largestIndex;
}
void QmitkImageNavigatorView::SetBorderColors()
{
if (m_IRenderWindowPart)
{
QString decoColor;
QmitkRenderWindow* renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("axial");
if (renderWindow)
{
decoColor = GetDecorationColorOfGeometry(renderWindow);
mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry();
if (geometry.IsNotNull())
{
mitk::Vector3D normal = geometry->GetNormal();
int axis = this->GetClosestAxisIndex(normal);
this->SetBorderColor(axis, decoColor);
}
}
renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("sagittal");
if (renderWindow)
{
decoColor = GetDecorationColorOfGeometry(renderWindow);
mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry();
if (geometry.IsNotNull())
{
mitk::Vector3D normal = geometry->GetNormal();
int axis = this->GetClosestAxisIndex(normal);
this->SetBorderColor(axis, decoColor);
}
}
renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("coronal");
if (renderWindow)
{
decoColor = GetDecorationColorOfGeometry(renderWindow);
mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry();
if (geometry.IsNotNull())
{
mitk::Vector3D normal = geometry->GetNormal();
int axis = this->GetClosestAxisIndex(normal);
this->SetBorderColor(axis, decoColor);
}
}
}
}
QString QmitkImageNavigatorView::GetDecorationColorOfGeometry(QmitkRenderWindow* renderWindow)
{
QColor color;
float rgb[3] = {1.0f, 1.0f, 1.0f};
float rgbMax = 255.0f;
mitk::BaseRenderer::GetInstance(renderWindow->GetVtkRenderWindow())->GetCurrentWorldPlaneGeometryNode()->GetColor(rgb);
color.setRed(static_cast<int>(rgb[0]*rgbMax + 0.5));
color.setGreen(static_cast<int>(rgb[1]*rgbMax + 0.5));
color.setBlue(static_cast<int>(rgb[2]*rgbMax + 0.5));
QString colorAsString = QString(color.name());
return colorAsString;
}
void QmitkImageNavigatorView::SetBorderColor(int axis, QString colorAsStyleSheetString)
{
if (axis == 0)
{
this->SetBorderColor(m_Controls.m_XWorldCoordinateSpinBox, colorAsStyleSheetString);
}
else if (axis == 1)
{
this->SetBorderColor(m_Controls.m_YWorldCoordinateSpinBox, colorAsStyleSheetString);
}
else if (axis == 2)
{
this->SetBorderColor(m_Controls.m_ZWorldCoordinateSpinBox, colorAsStyleSheetString);
}
}
void QmitkImageNavigatorView::SetBorderColor(QDoubleSpinBox *spinBox, QString colorAsStyleSheetString)
{
assert(spinBox);
spinBox->setStyleSheet(QString("border: 2px solid ") + colorAsStyleSheetString + ";");
}
void QmitkImageNavigatorView::SetStepSizes()
{
this->SetStepSize(0);
this->SetStepSize(1);
this->SetStepSize(2);
}
void QmitkImageNavigatorView::SetStepSize(int axis)
{
if (m_IRenderWindowPart)
{
mitk::BaseGeometry::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldGeometry3D();
if (geometry.IsNotNull())
{
mitk::Point3D crossPositionInIndexCoordinates;
mitk::Point3D crossPositionInIndexCoordinatesPlus1;
mitk::Point3D crossPositionInMillimetresPlus1;
mitk::Vector3D transformedAxisDirection;
mitk::Point3D crossPositionInMillimetres = m_IRenderWindowPart->GetSelectedPosition();
geometry->WorldToIndex(crossPositionInMillimetres, crossPositionInIndexCoordinates);
crossPositionInIndexCoordinatesPlus1 = crossPositionInIndexCoordinates;
crossPositionInIndexCoordinatesPlus1[axis] += 1;
geometry->IndexToWorld(crossPositionInIndexCoordinatesPlus1, crossPositionInMillimetresPlus1);
transformedAxisDirection = crossPositionInMillimetresPlus1 - crossPositionInMillimetres;
int closestAxisInMillimetreSpace = this->GetClosestAxisIndex(transformedAxisDirection);
double stepSize = transformedAxisDirection.GetNorm();
this->SetStepSize(closestAxisInMillimetreSpace, stepSize);
}
}
}
void QmitkImageNavigatorView::SetStepSize(int axis, double stepSize)
{
if (axis == 0)
{
m_Controls.m_XWorldCoordinateSpinBox->setSingleStep(stepSize);
}
else if (axis == 1)
{
m_Controls.m_YWorldCoordinateSpinBox->setSingleStep(stepSize);
}
else if (axis == 2)
{
m_Controls.m_ZWorldCoordinateSpinBox->setSingleStep(stepSize);
}
}
void QmitkImageNavigatorView::OnMillimetreCoordinateValueChanged()
{
if (m_IRenderWindowPart)
{
mitk::TimeGeometry::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldTimeGeometry();
if (geometry.IsNotNull())
{
mitk::Point3D positionInWorldCoordinates;
positionInWorldCoordinates[0] = m_Controls.m_XWorldCoordinateSpinBox->value();
positionInWorldCoordinates[1] = m_Controls.m_YWorldCoordinateSpinBox->value();
positionInWorldCoordinates[2] = m_Controls.m_ZWorldCoordinateSpinBox->value();
m_IRenderWindowPart->SetSelectedPosition(positionInWorldCoordinates);
}
}
}
void QmitkImageNavigatorView::OnRefetch()
{
if (m_IRenderWindowPart)
{
mitk::BaseGeometry::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldGeometry3D();
mitk::TimeGeometry::ConstPointer timeGeometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldTimeGeometry();
if (geometry.IsNull() && timeGeometry.IsNotNull())
{
mitk::TimeStepType timeStep = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetTime()->GetPos();
geometry = timeGeometry->GetGeometryForTimeStep(timeStep);
SetVisibilityOfTimeSlider(timeGeometry->CountTimeSteps());
}
if (geometry.IsNotNull())
{
mitk::BoundingBox::BoundsArrayType bounds = geometry->GetBounds();
mitk::Point3D cornerPoint1InIndexCoordinates;
cornerPoint1InIndexCoordinates[0] = bounds[0];
cornerPoint1InIndexCoordinates[1] = bounds[2];
cornerPoint1InIndexCoordinates[2] = bounds[4];
mitk::Point3D cornerPoint2InIndexCoordinates;
cornerPoint2InIndexCoordinates[0] = bounds[1];
cornerPoint2InIndexCoordinates[1] = bounds[3];
cornerPoint2InIndexCoordinates[2] = bounds[5];
if (!geometry->GetImageGeometry())
{
cornerPoint1InIndexCoordinates[0] += 0.5;
cornerPoint1InIndexCoordinates[1] += 0.5;
cornerPoint1InIndexCoordinates[2] += 0.5;
cornerPoint2InIndexCoordinates[0] -= 0.5;
cornerPoint2InIndexCoordinates[1] -= 0.5;
cornerPoint2InIndexCoordinates[2] -= 0.5;
}
mitk::Point3D crossPositionInWorldCoordinates = m_IRenderWindowPart->GetSelectedPosition();
mitk::Point3D cornerPoint1InWorldCoordinates;
mitk::Point3D cornerPoint2InWorldCoordinates;
geometry->IndexToWorld(cornerPoint1InIndexCoordinates, cornerPoint1InWorldCoordinates);
geometry->IndexToWorld(cornerPoint2InIndexCoordinates, cornerPoint2InWorldCoordinates);
m_Controls.m_XWorldCoordinateSpinBox->blockSignals(true);
m_Controls.m_YWorldCoordinateSpinBox->blockSignals(true);
m_Controls.m_ZWorldCoordinateSpinBox->blockSignals(true);
m_Controls.m_XWorldCoordinateSpinBox->setMinimum(std::min(cornerPoint1InWorldCoordinates[0], cornerPoint2InWorldCoordinates[0]));
m_Controls.m_YWorldCoordinateSpinBox->setMinimum(std::min(cornerPoint1InWorldCoordinates[1], cornerPoint2InWorldCoordinates[1]));
m_Controls.m_ZWorldCoordinateSpinBox->setMinimum(std::min(cornerPoint1InWorldCoordinates[2], cornerPoint2InWorldCoordinates[2]));
m_Controls.m_XWorldCoordinateSpinBox->setMaximum(std::max(cornerPoint1InWorldCoordinates[0], cornerPoint2InWorldCoordinates[0]));
m_Controls.m_YWorldCoordinateSpinBox->setMaximum(std::max(cornerPoint1InWorldCoordinates[1], cornerPoint2InWorldCoordinates[1]));
m_Controls.m_ZWorldCoordinateSpinBox->setMaximum(std::max(cornerPoint1InWorldCoordinates[2], cornerPoint2InWorldCoordinates[2]));
m_Controls.m_XWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[0]);
m_Controls.m_YWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[1]);
m_Controls.m_ZWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[2]);
m_Controls.m_XWorldCoordinateSpinBox->blockSignals(false);
m_Controls.m_YWorldCoordinateSpinBox->blockSignals(false);
m_Controls.m_ZWorldCoordinateSpinBox->blockSignals(false);
/// Calculating 'inverse direction' property.
mitk::AffineTransform3D::MatrixType matrix = geometry->GetIndexToWorldTransform()->GetMatrix();
matrix.GetVnlMatrix().normalize_columns();
mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse();
for (int worldAxis = 0; worldAxis < 3; ++worldAxis)
{
QmitkRenderWindow* renderWindow =
worldAxis == 0 ? m_IRenderWindowPart->GetQmitkRenderWindow("sagittal") :
worldAxis == 1 ? m_IRenderWindowPart->GetQmitkRenderWindow("coronal") :
m_IRenderWindowPart->GetQmitkRenderWindow("axial");
if (renderWindow)
{
const mitk::BaseGeometry* rendererGeometry = renderWindow->GetRenderer()->GetCurrentWorldGeometry();
/// Because of some problems with the current way of event signalling,
/// 'Modified' events are sent out from the stepper while the renderer
/// does not have a geometry yet. Therefore, we do a nullptr check here.
/// See bug T22122. This check can be resolved after T22122 got fixed.
if (rendererGeometry)
{
int dominantAxis = itk::Function::Max3(
inverseMatrix[0][worldAxis],
inverseMatrix[1][worldAxis],
inverseMatrix[2][worldAxis]);
bool referenceGeometryAxisInverted = inverseMatrix[dominantAxis][worldAxis] < 0;
bool rendererZAxisInverted = rendererGeometry->GetAxisVector(2)[worldAxis] < 0;
/// `referenceGeometryAxisInverted` tells if the direction of the corresponding axis
/// of the reference geometry is flipped compared to the 'world direction' or not.
///
/// `rendererZAxisInverted` tells if direction of the renderer geometry z axis is
/// flipped compared to the 'world direction' or not. This is the same as the indexing
/// direction in the slice navigation controller and matches the 'top' property when
/// initialising the renderer planes. (If 'top' was true then the direction is
/// inverted.)
///
/// The world direction can be +1 ('up') that means right, anterior or superior, or
/// it can be -1 ('down') that means left, posterior or inferior, respectively.
///
/// If these two do not match, we have to invert the index between the slice navigation
/// controller and the slider navigator widget, so that the user can see and control
/// the index according to the reference geometry, rather than the slice navigation
/// controller. The index in the slice navigation controller depends on in which way
/// the reference geometry has been resliced for the renderer, and it does not necessarily
/// match neither the world direction, nor the direction of the corresponding axis of
/// the reference geometry. Hence, it is a merely internal information that should not
/// be exposed to the GUI.
///
/// So that one can navigate in the same world direction by dragging the slider
/// right, regardless of the direction of the corresponding axis of the reference
/// geometry, we invert the direction of the controls if the reference geometry axis
/// is inverted but the direction is not ('inversDirection' is false) or the other
/// way around.
bool inverseDirection = referenceGeometryAxisInverted != rendererZAxisInverted;
QmitkSliderNavigatorWidget* navigatorWidget =
worldAxis == 0 ? m_Controls.m_SliceNavigatorSagittal :
worldAxis == 1 ? m_Controls.m_SliceNavigatorFrontal :
m_Controls.m_SliceNavigatorAxial;
navigatorWidget->SetInverseDirection(inverseDirection);
// This should be a preference (see T22254)
// bool invertedControls = referenceGeometryAxisInverted != inverseDirection;
// navigatorWidget->SetInvertedControls(invertedControls);
}
}
}
}
this->SetBorderColors();
}
}
void QmitkImageNavigatorView::SetVisibilityOfTimeSlider(std::size_t timeSteps)
{
m_Controls.m_SliceNavigatorTime->setVisible(timeSteps > 1);
m_Controls.m_TimeLabel->setVisible(timeSteps > 1);
}
diff --git a/Plugins/org.mitk.gui.qt.matchpoint.visualizer/src/internal/QmitkMatchPointRegistrationVisualizer.cpp b/Plugins/org.mitk.gui.qt.matchpoint.visualizer/src/internal/QmitkMatchPointRegistrationVisualizer.cpp
index 49fdf85bc0..9f9ea6f8ac 100644
--- a/Plugins/org.mitk.gui.qt.matchpoint.visualizer/src/internal/QmitkMatchPointRegistrationVisualizer.cpp
+++ b/Plugins/org.mitk.gui.qt.matchpoint.visualizer/src/internal/QmitkMatchPointRegistrationVisualizer.cpp
@@ -1,798 +1,798 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "org_mitk_gui_qt_matchpoint_visualizer_Activator.h"
// Blueberry
#include <berryISelectionService.h>
#include <berryIWorkbenchWindow.h>
// Mitk
#include <mitkStatusBar.h>
#include <mitkProperties.h>
#include <mitkColorProperty.h>
#include <mitkNodePredicateDataType.h>
#include <mitkNodePredicateAnd.h>
#include <mitkNodePredicateDataProperty.h>
#include <mitkNodePredicateFunction.h>
#include "mitkRegVisDirectionProperty.h"
#include "mitkRegVisStyleProperty.h"
#include "mitkRegVisColorStyleProperty.h"
#include "mitkRegVisPropertyTags.h"
#include "mitkRegVisHelper.h"
#include "mitkMatchPointPropertyTags.h"
#include "mitkRegistrationHelper.h"
// Qmitk
#include "QmitkMatchPointRegistrationVisualizer.h"
// Qt
#include <QMessageBox>
#include <QErrorMessage>
const std::string QmitkMatchPointRegistrationVisualizer::VIEW_ID =
"org.mitk.views.matchpoint.visualizer";
QmitkMatchPointRegistrationVisualizer::QmitkMatchPointRegistrationVisualizer()
: m_Parent(nullptr), m_internalUpdateGuard(false), m_spSelectedFOVRefNode(nullptr),
m_spSelectedRegNode(nullptr)
{
}
void QmitkMatchPointRegistrationVisualizer::SetFocus()
{
}
void QmitkMatchPointRegistrationVisualizer::CreateConnections()
{
connect(m_Controls->registrationNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointRegistrationVisualizer::OnNodeSelectionChanged);
connect(m_Controls->fovReferenceNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointRegistrationVisualizer::OnNodeSelectionChanged);
connect(m_Controls->m_pbStyleGrid, SIGNAL(clicked()), this, SLOT(OnStyleButtonPushed()));
connect(m_Controls->m_pbStyleGlyph, SIGNAL(clicked()), this, SLOT(OnStyleButtonPushed()));
connect(m_Controls->m_pbStylePoints, SIGNAL(clicked()), this, SLOT(OnStyleButtonPushed()));
connect(m_Controls->m_comboDirection, SIGNAL(currentIndexChanged(int)), this,
SLOT(OnDirectionChanged(int)));
connect(m_Controls->m_pbUpdateViz, SIGNAL(clicked()), this, SLOT(OnUpdateBtnPushed()));
connect(m_Controls->radioColorUni, SIGNAL(toggled(bool)), m_Controls->btnUniColor,
SLOT(setEnabled(bool)));
connect(m_Controls->radioColorVecMag, SIGNAL(toggled(bool)), m_Controls->groupColorCoding,
SLOT(setEnabled(bool)));
connect(m_Controls->m_pbStyleGrid, SIGNAL(toggled(bool)), m_Controls->tabGrid,
SLOT(setEnabled(bool)));
connect(m_Controls->cbVevMagInterlolate, SIGNAL(toggled(bool)), this,
SLOT(OnColorInterpolationChecked(bool)));
connect(m_Controls->m_checkUseRefSize, SIGNAL(clicked()), this, SLOT(TransferFOVRefGeometry()));
connect(m_Controls->m_checkUseRefSpacing, SIGNAL(clicked()), this, SLOT(TransferFOVRefGeometry()));
connect(m_Controls->m_checkUseRefOrigin, SIGNAL(clicked()), this, SLOT(TransferFOVRefGeometry()));
connect(m_Controls->m_checkUseRefOrientation, SIGNAL(clicked()), this, SLOT(TransferFOVRefGeometry()));
}
void QmitkMatchPointRegistrationVisualizer::Error(QString msg)
{
mitk::StatusBar::GetInstance()->DisplayErrorText(msg.toLatin1());
MITK_ERROR << msg.toStdString().c_str();
}
void QmitkMatchPointRegistrationVisualizer::CreateQtPartControl(QWidget* parent)
{
m_Controls = new Ui::MatchPointRegVisControls;
// create GUI widgets from the Qt Designer's .ui file
m_Controls->setupUi(parent);
m_Parent = parent;
this->m_Controls->registrationNodeSelector->SetDataStorage(this->GetDataStorage());
this->m_Controls->registrationNodeSelector->SetSelectionIsOptional(false);
this->m_Controls->fovReferenceNodeSelector->SetDataStorage(this->GetDataStorage());
this->m_Controls->fovReferenceNodeSelector->SetSelectionIsOptional(false);
m_Controls->registrationNodeSelector->SetInvalidInfo("Select registration.");
m_Controls->registrationNodeSelector->SetPopUpTitel("Select registration.");
m_Controls->registrationNodeSelector->SetPopUpHint("Select the registration object whose registration visualization should be edited.");
m_Controls->fovReferenceNodeSelector->SetInvalidInfo("Select a FOV reference image.");
m_Controls->fovReferenceNodeSelector->SetPopUpTitel("Select a FOV reference image.");
m_Controls->fovReferenceNodeSelector->SetPopUpHint("Select the the image that should be used to define the field of view (FOV) for the registration visualization. The visualization will use the image geometry (size, orientation, spacing...).");
this->ConfigureNodePredicates();
this->m_Controls->btnVecMagColorSmall->setDisplayColorName(false);
this->m_Controls->btnVecMagColorMedium->setDisplayColorName(false);
this->m_Controls->btnVecMagColorLarge->setDisplayColorName(false);
this->m_Controls->btnVecMagColorNeg->setDisplayColorName(false);
this->m_Controls->btnUniColor->setDisplayColorName(false);
this->m_Controls->btnStartGridColor->setDisplayColorName(false);
this->CreateConnections();
this->m_Controls->radioColorUni->setChecked(false);
this->m_Controls->radioColorVecMag->setChecked(true);
this->CheckInputs();
this->LoadStateFromNode();
this->ConfigureVisualizationControls();
//deactivate because currently not an implemented style
this->m_Controls->m_pbStylePoints->setVisible(false);
}
void QmitkMatchPointRegistrationVisualizer::ConfigureNodePredicates()
{
m_Controls->registrationNodeSelector->SetNodePredicate(mitk::MITKRegistrationHelper::RegNodePredicate());
auto geometryCheck = [](const mitk::DataNode * node)
{
return node->GetData() && node->GetData()->GetGeometry();
};
mitk::NodePredicateFunction::Pointer hasGeometry = mitk::NodePredicateFunction::New(geometryCheck);
auto nodePredicate = mitk::NodePredicateAnd::New(mitk::MITKRegistrationHelper::ImageNodePredicate().GetPointer(), hasGeometry.GetPointer());
m_Controls->fovReferenceNodeSelector->SetNodePredicate(nodePredicate.GetPointer());
}
mitk::MAPRegistrationWrapper* QmitkMatchPointRegistrationVisualizer::GetCurrentRegistration()
{
mitk::MAPRegistrationWrapper* result = nullptr;
if (this->m_spSelectedRegNode.IsNotNull())
{
result = dynamic_cast<mitk::MAPRegistrationWrapper*>(this->m_spSelectedRegNode->GetData());
assert(result);
}
return result;
}
mitk::DataNode::Pointer QmitkMatchPointRegistrationVisualizer::GetSelectedRegNode() const
{
return m_Controls->registrationNodeSelector->GetSelectedNode();
}
mitk::DataNode::Pointer QmitkMatchPointRegistrationVisualizer::GetRefNodeOfReg(bool target) const
{
mitk::DataNode::Pointer spResult = nullptr;
if (this->m_spSelectedRegNode.IsNotNull() && m_spSelectedRegNode->GetData())
{
std::string nodeName;
mitk::BaseProperty* uidProp;
if (target)
{
uidProp = m_spSelectedRegNode->GetData()->GetProperty(mitk::Prop_RegAlgTargetData);
}
else
{
uidProp = m_spSelectedRegNode->GetData()->GetProperty(mitk::Prop_RegAlgMovingData);
}
if (uidProp)
{
//search for the target node
mitk::NodePredicateDataProperty::Pointer predicate = mitk::NodePredicateDataProperty::New(mitk::Prop_UID,
uidProp);
spResult = this->GetDataStorage()->GetNode(predicate);
}
}
return spResult;
}
mitk::DataNode::Pointer QmitkMatchPointRegistrationVisualizer::GetSelectedDataNode()
{
return m_Controls->fovReferenceNodeSelector->GetSelectedNode();
}
void QmitkMatchPointRegistrationVisualizer::CheckInputs()
{
this->m_spSelectedRegNode = this->GetSelectedRegNode();
this->InitRegNode();
this->m_spSelectedFOVRefNode = this->GetSelectedDataNode();
}
void QmitkMatchPointRegistrationVisualizer::ConfigureVisualizationControls()
{
if (!m_internalUpdateGuard)
{
m_internalUpdateGuard = true;
m_Controls->groupViz->setVisible(this->m_spSelectedRegNode.IsNotNull());
m_Controls->m_pbUpdateViz->setEnabled(this->m_spSelectedRegNode.IsNotNull());
m_Controls->m_boxSettings->setEnabled(this->m_spSelectedRegNode.IsNotNull());
m_Controls->m_boxStyles->setEnabled(this->m_spSelectedRegNode.IsNotNull());
this->ActualizeRegInfo(this->GetCurrentRegistration());
this->m_Controls->m_checkUseRefSize->setEnabled(this->m_spSelectedRegNode.IsNotNull()
&& this->m_spSelectedFOVRefNode.IsNotNull());
this->m_Controls->m_checkUseRefOrigin->setEnabled(this->m_spSelectedRegNode.IsNotNull()
&& this->m_spSelectedFOVRefNode.IsNotNull());
this->m_Controls->m_checkUseRefSpacing->setEnabled(this->m_spSelectedRegNode.IsNotNull()
&& this->m_spSelectedFOVRefNode.IsNotNull());
m_internalUpdateGuard = false;
}
}
void QmitkMatchPointRegistrationVisualizer::StoreStateInNode()
{
if (this->m_spSelectedRegNode.IsNotNull())
{
//general
this->m_spSelectedRegNode->SetProperty(mitk::nodeProp_RegVisDirection,
mitk::RegVisDirectionProperty::New(this->m_Controls->m_comboDirection->currentIndex()));
this->m_spSelectedRegNode->SetBoolProperty(mitk::nodeProp_RegVisGrid,
this->m_Controls->m_pbStyleGrid->isChecked());
this->m_spSelectedRegNode->SetBoolProperty(mitk::nodeProp_RegVisGlyph,
this->m_Controls->m_pbStyleGlyph->isChecked());
this->m_spSelectedRegNode->SetBoolProperty(mitk::nodeProp_RegVisPoints,
this->m_Controls->m_pbStylePoints->isChecked());
//Visualization
if (this->m_Controls->radioColorUni->isChecked())
{
this->m_spSelectedRegNode->SetProperty(mitk::nodeProp_RegVisColorStyle,
mitk::RegVisColorStyleProperty::New(0));
}
else
{
this->m_spSelectedRegNode->SetProperty(mitk::nodeProp_RegVisColorStyle,
mitk::RegVisColorStyleProperty::New(1));
}
float tmpColor[3];
tmpColor[0] = this->m_Controls->btnUniColor->color().redF();
tmpColor[1] = this->m_Controls->btnUniColor->color().greenF();
tmpColor[2] = this->m_Controls->btnUniColor->color().blueF();
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColorUni,
mitk::ColorProperty::New(tmpColor), nullptr, true);
tmpColor[0] = this->m_Controls->btnVecMagColorNeg->color().redF();
tmpColor[1] = this->m_Controls->btnVecMagColorNeg->color().greenF();
tmpColor[2] = this->m_Controls->btnVecMagColorNeg->color().blueF();
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor1Value,
mitk::ColorProperty::New(tmpColor), nullptr, true);
tmpColor[0] = this->m_Controls->btnVecMagColorSmall->color().redF();
tmpColor[1] = this->m_Controls->btnVecMagColorSmall->color().greenF();
tmpColor[2] = this->m_Controls->btnVecMagColorSmall->color().blueF();
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor2Value,
mitk::ColorProperty::New(tmpColor), nullptr, true);
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor2Magnitude,
mitk::DoubleProperty::New(this->m_Controls->sbVecMagSmall->value()), nullptr, true);
tmpColor[0] = this->m_Controls->btnVecMagColorMedium->color().redF();
tmpColor[1] = this->m_Controls->btnVecMagColorMedium->color().greenF();
tmpColor[2] = this->m_Controls->btnVecMagColorMedium->color().blueF();
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor3Value,
mitk::ColorProperty::New(tmpColor), nullptr, true);
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor3Magnitude,
mitk::DoubleProperty::New(this->m_Controls->sbVecMagMedium->value()), nullptr, true);
tmpColor[0] = this->m_Controls->btnVecMagColorLarge->color().redF();
tmpColor[1] = this->m_Controls->btnVecMagColorLarge->color().greenF();
tmpColor[2] = this->m_Controls->btnVecMagColorLarge->color().blueF();
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor4Value,
mitk::ColorProperty::New(tmpColor), nullptr, true);
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor4Magnitude,
mitk::DoubleProperty::New(this->m_Controls->sbVecMagLarge->value()), nullptr, true);
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColorInterpolate,
mitk::BoolProperty::New(this->m_Controls->cbVevMagInterlolate->isChecked()), nullptr, true);
//Grid Settings
this->m_spSelectedRegNode->SetIntProperty(mitk::nodeProp_RegVisGridFrequence,
this->m_Controls->m_sbGridFrequency->value());
this->m_spSelectedRegNode->SetBoolProperty(mitk::nodeProp_RegVisGridShowStart,
this->m_Controls->m_groupShowStartGrid->isChecked());
tmpColor[0] = this->m_Controls->btnStartGridColor->color().redF();
tmpColor[1] = this->m_Controls->btnStartGridColor->color().greenF();
tmpColor[2] = this->m_Controls->btnStartGridColor->color().blueF();
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisGridStartColor,
mitk::ColorProperty::New(tmpColor), nullptr, true);
//FOV
mitk::Vector3D value;
value[0] = this->m_Controls->m_sbFOVSizeX->value();
value[1] = this->m_Controls->m_sbFOVSizeY->value();
value[2] = this->m_Controls->m_sbFOVSizeZ->value();
this->m_spSelectedRegNode->SetProperty(mitk::nodeProp_RegVisFOVSize,
mitk::Vector3DProperty::New(value));
value[0] = this->m_Controls->m_sbGridSpX->value();
value[1] = this->m_Controls->m_sbGridSpY->value();
value[2] = this->m_Controls->m_sbGridSpZ->value();
this->m_spSelectedRegNode->SetProperty(mitk::nodeProp_RegVisFOVSpacing,
mitk::Vector3DProperty::New(value));
mitk::Point3D origin;
origin[0] = this->m_Controls->m_sbFOVOriginX->value();
origin[1] = this->m_Controls->m_sbFOVOriginY->value();
origin[2] = this->m_Controls->m_sbFOVOriginZ->value();
this->m_spSelectedRegNode->SetProperty(mitk::nodeProp_RegVisFOVOrigin,
mitk::Point3dProperty::New(origin));
mitk::Vector3D orientationRow1;
mitk::Vector3D orientationRow2;
mitk::Vector3D orientationRow3;
- orientationRow1.SetVnlVector(m_FOVRefOrientation.GetVnlMatrix().get_row(0));
- orientationRow2.SetVnlVector(m_FOVRefOrientation.GetVnlMatrix().get_row(1));
- orientationRow3.SetVnlVector(m_FOVRefOrientation.GetVnlMatrix().get_row(2));
+ orientationRow1.SetVnlVector(m_FOVRefOrientation.GetVnlMatrix().get_row(0).as_ref());
+ orientationRow2.SetVnlVector(m_FOVRefOrientation.GetVnlMatrix().get_row(1).as_ref());
+ orientationRow3.SetVnlVector(m_FOVRefOrientation.GetVnlMatrix().get_row(2).as_ref());
this->m_spSelectedRegNode->SetProperty(mitk::nodeProp_RegVisFOVOrientation1,
mitk::Vector3DProperty::New(orientationRow1));
this->m_spSelectedRegNode->SetProperty(mitk::nodeProp_RegVisFOVOrientation2,
mitk::Vector3DProperty::New(orientationRow2));
this->m_spSelectedRegNode->SetProperty(mitk::nodeProp_RegVisFOVOrientation3,
mitk::Vector3DProperty::New(orientationRow3));
}
}
void QmitkMatchPointRegistrationVisualizer::LoadStateFromNode()
{
if (this->m_spSelectedRegNode.IsNotNull())
{
mitk::RegVisDirectionProperty* directionProp = nullptr;
if (this->m_spSelectedRegNode->GetProperty(directionProp, mitk::nodeProp_RegVisDirection))
{
this->m_Controls->m_comboDirection->setCurrentIndex(directionProp->GetValueAsId());
}
else
{
this->Error(QStringLiteral("Cannot configure plugin controlls correctly. Node property ") + QString(
mitk::nodeProp_RegVisDirection) + QStringLiteral(" has not the assumed type."));
}
bool styleActive = false;
if (this->m_spSelectedRegNode->GetBoolProperty(mitk::nodeProp_RegVisGrid, styleActive))
{
this->m_Controls->m_pbStyleGrid->setChecked(styleActive);
this->m_Controls->tabGrid->setEnabled(styleActive);
}
else
{
this->Error(QStringLiteral("Cannot configure plugin controlls correctly. Node property ") + QString(
mitk::nodeProp_RegVisGrid) + QStringLiteral(" has not the assumed type."));
}
if (this->m_spSelectedRegNode->GetBoolProperty(mitk::nodeProp_RegVisGlyph, styleActive))
{
this->m_Controls->m_pbStyleGlyph->setChecked(styleActive);
}
else
{
this->Error(QStringLiteral("Cannot configure plugin controlls correctly. Node property ") + QString(
mitk::nodeProp_RegVisGlyph) + QStringLiteral(" has not the assumed type."));
}
if (this->m_spSelectedRegNode->GetBoolProperty(mitk::nodeProp_RegVisPoints, styleActive))
{
this->m_Controls->m_pbStylePoints->setChecked(styleActive);
}
else
{
this->Error(QStringLiteral("Cannot configure plugin controlls correctly. Node property ") + QString(
mitk::nodeProp_RegVisPoints) + QStringLiteral(" has not the assumed type."));
}
///////////////////////////////////////////////////////
//visualization
mitk::RegVisColorStyleProperty* colorStyleProp = nullptr;
if (this->m_spSelectedRegNode->GetProperty(colorStyleProp, mitk::nodeProp_RegVisColorStyle))
{
this->m_Controls->radioColorUni->setChecked(colorStyleProp->GetValueAsId() == 0);
this->m_Controls->radioColorVecMag->setChecked(colorStyleProp->GetValueAsId() == 1);
}
else
{
this->Error(QStringLiteral("Cannot configure plugin controlls correctly. Node property ") + QString(
mitk::nodeProp_RegVisColorStyle) + QStringLiteral(" has not the assumed type."));
}
QColor tmpColor;
float colorUni[3] = { 0.0, 0.0, 0.0 };
this->m_spSelectedRegNode->GetColor(colorUni, nullptr, mitk::nodeProp_RegVisColorUni);
tmpColor.setRgbF(colorUni[0], colorUni[1], colorUni[2]);
this->m_Controls->btnUniColor->setColor(tmpColor);
float color1[3] = { 0.0, 0.0, 0.0 };
this->m_spSelectedRegNode->GetColor(color1, nullptr, mitk::nodeProp_RegVisColor1Value);
tmpColor.setRgbF(color1[0], color1[1], color1[2]);
this->m_Controls->btnVecMagColorNeg->setColor(tmpColor);
float color2[3] = { 0.25, 0.25, 0.25 };
this->m_spSelectedRegNode->GetColor(color2, nullptr, mitk::nodeProp_RegVisColor2Value);
tmpColor.setRgbF(color2[0], color2[1], color2[2]);
this->m_Controls->btnVecMagColorSmall->setColor(tmpColor);
float color3[3] = { 0.5, 0.5, 0.5 };
this->m_spSelectedRegNode->GetColor(color3, nullptr, mitk::nodeProp_RegVisColor3Value);
tmpColor.setRgbF(color3[0], color3[1], color3[2]);
this->m_Controls->btnVecMagColorMedium->setColor(tmpColor);
float color4[3] = { 1.0, 1.0, 1.0 };
this->m_spSelectedRegNode->GetColor(color4, nullptr, mitk::nodeProp_RegVisColor4Value);
tmpColor.setRgbF(color4[0], color4[1], color4[2]);
this->m_Controls->btnVecMagColorLarge->setColor(tmpColor);
double mag2 = 0;
this->m_spSelectedRegNode->GetPropertyValue(mitk::nodeProp_RegVisColor2Magnitude, mag2);
double mag3 = 0;
this->m_spSelectedRegNode->GetPropertyValue(mitk::nodeProp_RegVisColor3Magnitude, mag3);
double mag4 = 0;
this->m_spSelectedRegNode->GetPropertyValue(mitk::nodeProp_RegVisColor4Magnitude, mag4);
bool interpolate = true;
this->m_spSelectedRegNode->GetBoolProperty(mitk::nodeProp_RegVisColorInterpolate, interpolate);
this->m_Controls->sbVecMagSmall->setValue(mag2);
this->m_Controls->sbVecMagMedium->setValue(mag3);
this->m_Controls->sbVecMagLarge->setValue(mag4);
this->m_Controls->cbVevMagInterlolate->setChecked(interpolate);
///////////////////////////////////////////////////////
//Grid general
bool showStart = false;
if (this->m_spSelectedRegNode->GetBoolProperty(mitk::nodeProp_RegVisGridShowStart, showStart))
{
this->m_Controls->m_groupShowStartGrid->setChecked(showStart);
}
else
{
this->Error(QStringLiteral("Cannot configure plugin controlls correctly. Node property ") + QString(
mitk::nodeProp_RegVisGridShowStart) + QStringLiteral(" is not correctly defined."));
}
int gridFrequ = 5;
if (this->m_spSelectedRegNode->GetIntProperty(mitk::nodeProp_RegVisGridFrequence, gridFrequ))
{
this->m_Controls->m_sbGridFrequency->setValue(gridFrequ);
}
else
{
this->Error(QStringLiteral("Cannot configure plugin controlls correctly. Node property ") + QString(
mitk::nodeProp_RegVisGridFrequence) + QStringLiteral(" is not correctly defined."));
}
float colorStart[3] = { 0.0, 0.0, 0.0 };
this->m_spSelectedRegNode->GetColor(colorStart, nullptr, mitk::nodeProp_RegVisGridStartColor);
tmpColor.setRgbF(colorStart[0], colorStart[1], colorStart[2]);
this->m_Controls->btnStartGridColor->setColor(tmpColor);
///////////////////////////////////////////////////////
//FOV
mitk::Vector3DProperty* valueProp = nullptr;
if (this->m_spSelectedRegNode->GetProperty(valueProp, mitk::nodeProp_RegVisFOVSize))
{
this->m_Controls->m_sbFOVSizeX->setValue(valueProp->GetValue()[0]);
this->m_Controls->m_sbFOVSizeY->setValue(valueProp->GetValue()[1]);
this->m_Controls->m_sbFOVSizeZ->setValue(valueProp->GetValue()[2]);
}
else
{
this->Error(QStringLiteral("Cannot configure plugin controlls correctly. Node property ") + QString(
mitk::nodeProp_RegVisFOVSize) + QStringLiteral(" is not correctly defined."));
}
if (this->m_spSelectedRegNode->GetProperty(valueProp, mitk::nodeProp_RegVisFOVSpacing))
{
this->m_Controls->m_sbGridSpX->setValue(valueProp->GetValue()[0]);
this->m_Controls->m_sbGridSpY->setValue(valueProp->GetValue()[1]);
this->m_Controls->m_sbGridSpZ->setValue(valueProp->GetValue()[2]);
}
else
{
this->Error(QStringLiteral("Cannot configure plugin controlls correctly. Node property ") + QString(
mitk::nodeProp_RegVisFOVSpacing) + QStringLiteral(" is not correctly defined."));
}
mitk::Point3dProperty* originProp = nullptr;
if (this->m_spSelectedRegNode->GetProperty(originProp, mitk::nodeProp_RegVisFOVOrigin))
{
this->m_Controls->m_sbFOVOriginX->setValue(originProp->GetValue()[0]);
this->m_Controls->m_sbFOVOriginY->setValue(originProp->GetValue()[1]);
this->m_Controls->m_sbFOVOriginZ->setValue(originProp->GetValue()[2]);
}
else
{
this->Error(QStringLiteral("Cannot configure plugin controlls correctly. Node property ") + QString(
mitk::nodeProp_RegVisFOVOrigin) + QStringLiteral(" is not correctly defined."));
}
mitk::Vector3DProperty* orientationProp1;
mitk::Vector3DProperty* orientationProp2;
mitk::Vector3DProperty* orientationProp3;
if (this->m_spSelectedRegNode->GetProperty(orientationProp1, mitk::nodeProp_RegVisFOVOrientation1) &&
this->m_spSelectedRegNode->GetProperty(orientationProp2, mitk::nodeProp_RegVisFOVOrientation2) &&
this->m_spSelectedRegNode->GetProperty(orientationProp3, mitk::nodeProp_RegVisFOVOrientation3))
{
this->m_Controls->m_sbFOVOriginX->setValue(originProp->GetValue()[0]);
this->m_Controls->m_sbFOVOriginY->setValue(originProp->GetValue()[1]);
this->m_Controls->m_sbFOVOriginZ->setValue(originProp->GetValue()[2]);
m_FOVRefOrientation.GetVnlMatrix().set_row(0, orientationProp1->GetValue().GetVnlVector());
m_FOVRefOrientation.GetVnlMatrix().set_row(1, orientationProp2->GetValue().GetVnlVector());
m_FOVRefOrientation.GetVnlMatrix().set_row(2, orientationProp3->GetValue().GetVnlVector());
}
else
{
m_FOVRefOrientation.SetIdentity();
this->Error(QStringLiteral("Cannot configure plugin controlls correctly. One of the node propertiesy ") +
QString(mitk::nodeProp_RegVisFOVOrientation1) + QString(mitk::nodeProp_RegVisFOVOrientation2) +
QString(mitk::nodeProp_RegVisFOVOrientation3) + QStringLiteral(" is not correctly defined."));
}
this->UpdateOrientationMatrixWidget();
}
}
void QmitkMatchPointRegistrationVisualizer::CheckAndSetDefaultFOVRef()
{
//check if node has a default reference node.
mitk::DataNode::Pointer defaultRef = this->GetRefNodeOfReg(
this->m_Controls->m_comboDirection->currentIndex() ==
1); //direction value 1 = show inverse mapping -> we need the target image used for the registration.
//if there is a default node and no m_spSelectedFOVRefNode is set -> set default node and transfer values
if (defaultRef.IsNotNull() && this->m_spSelectedFOVRefNode.IsNull())
{
//there is a default ref and no ref lock -> select default ref and transfer its values
this->m_spSelectedFOVRefNode = defaultRef;
QmitkSingleNodeSelectionWidget::NodeList selection({ defaultRef });
this->m_Controls->fovReferenceNodeSelector->SetCurrentSelection(selection);
this->m_Controls->m_checkUseRefSize->setChecked(true);
this->m_Controls->m_checkUseRefOrigin->setChecked(true);
this->m_Controls->m_checkUseRefSpacing->setChecked(true);
this->m_Controls->m_checkUseRefOrientation->setChecked(true);
}
if (this->m_spSelectedFOVRefNode.IsNotNull())
{
//auto transfere values
this->TransferFOVRefGeometry();
}
}
void QmitkMatchPointRegistrationVisualizer::OnNodeSelectionChanged(QList<mitk::DataNode::Pointer> /*nodes*/)
{
this->CheckInputs();
this->LoadStateFromNode();
this->CheckAndSetDefaultFOVRef();
this->ConfigureVisualizationControls();
}
void QmitkMatchPointRegistrationVisualizer::ActualizeRegInfo(mitk::MAPRegistrationWrapper*
currentReg)
{
std::stringstream descriptionString;
m_Controls->m_teRegInfo->clear();
if (currentReg)
{
descriptionString << "Moving dimension: " << currentReg->GetMovingDimensions() << "<br/>";
descriptionString << "Target dimension: " << currentReg->GetTargetDimensions() << "<br/>";
descriptionString << "Limited moving representation: " <<
currentReg->HasLimitedMovingRepresentation() << "<br/>";
descriptionString << "Limited target representation: " <<
currentReg->HasLimitedTargetRepresentation() << "<br/>";
mitk::MAPRegistrationWrapper::TagMapType tagMap = currentReg->GetTags();
descriptionString << "<br/><b>Tags:</b><br/>";
for (mitk::MAPRegistrationWrapper::TagMapType::const_iterator pos = tagMap.begin();
pos != tagMap.end(); ++pos)
{
descriptionString << pos->first << " : " << pos->second << "<br/>";
}
}
else
{
descriptionString << "<font color='red'>no registration selected!</font>";
}
m_Controls->m_teRegInfo->insertHtml(QString::fromStdString(descriptionString.str()));
}
void QmitkMatchPointRegistrationVisualizer::OnDirectionChanged(int)
{
this->CheckAndSetDefaultFOVRef();
this->ConfigureVisualizationControls();
}
void QmitkMatchPointRegistrationVisualizer::OnUpdateBtnPushed()
{
this->StoreStateInNode();
mitk::Geometry3D::Pointer gridDesc;
unsigned int gridFrequ = 5;
mitk::GetGridGeometryFromNode(this->m_spSelectedRegNode, gridDesc, gridFrequ);
this->GetCurrentRegistration()->SetGeometry(gridDesc);
mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll();
}
void QmitkMatchPointRegistrationVisualizer::OnStyleButtonPushed()
{
}
void QmitkMatchPointRegistrationVisualizer::OnColorInterpolationChecked(bool checked)
{
if (checked)
{
this->m_Controls->labelVecMagSmall->setText(QStringLiteral("="));
this->m_Controls->labelVecMagMedium->setText(QStringLiteral("="));
this->m_Controls->labelVecMagLarge->setText(QStringLiteral("="));
}
else
{
this->m_Controls->labelVecMagSmall->setText(QStringLiteral(">"));
this->m_Controls->labelVecMagMedium->setText(QStringLiteral(">"));
this->m_Controls->labelVecMagLarge->setText(QStringLiteral(">"));
}
}
mitk::ScalarType QmitkMatchPointRegistrationVisualizer::GetSaveSpacing(mitk::ScalarType gridRes,
mitk::ScalarType spacing, unsigned int maxGridRes) const
{
mitk::ScalarType newSpacing = spacing;
mitk::ScalarType scaling = gridRes / maxGridRes;
if (scaling > 1.0)
{
newSpacing = spacing * scaling;
}
return newSpacing;
}
void QmitkMatchPointRegistrationVisualizer::TransferFOVRefGeometry()
{
if (this->m_spSelectedFOVRefNode.IsNotNull())
{
assert(this->m_spSelectedFOVRefNode->GetData());
assert(this->m_spSelectedFOVRefNode->GetData()->GetGeometry());
mitk::BaseGeometry* gridRef = this->m_spSelectedFOVRefNode->GetData()->GetGeometry();
mitk::Vector3D spacing = gridRef->GetSpacing();
mitk::Point3D origin = gridRef->GetOrigin();
mitk::Geometry3D::BoundsArrayType bounds = gridRef->GetBounds();
mitk::AffineTransform3D::ConstPointer fovTransform = gridRef->GetIndexToWorldTransform();
if (this->m_Controls->m_checkUseRefSize->isChecked())
{
this->m_Controls->m_sbFOVSizeX->setValue((bounds[1] - bounds[0])*spacing[0]);
this->m_Controls->m_sbFOVSizeY->setValue((bounds[3] - bounds[2])*spacing[1]);
this->m_Controls->m_sbFOVSizeZ->setValue((bounds[5] - bounds[4])*spacing[2]);
}
if (this->m_Controls->m_checkUseRefSpacing->isChecked())
{
this->m_Controls->m_sbGridSpX->setValue(GetSaveSpacing((bounds[1] - bounds[0]), spacing[0], 20));
this->m_Controls->m_sbGridSpY->setValue(GetSaveSpacing((bounds[3] - bounds[2]), spacing[1], 20));
this->m_Controls->m_sbGridSpZ->setValue(GetSaveSpacing((bounds[5] - bounds[4]), spacing[2], 20));
}
if (this->m_Controls->m_checkUseRefOrigin->isChecked())
{
this->m_Controls->m_sbFOVOriginX->setValue(origin[0]);
this->m_Controls->m_sbFOVOriginY->setValue(origin[1]);
this->m_Controls->m_sbFOVOriginZ->setValue(origin[2]);
}
if (this->m_Controls->m_checkUseRefOrientation->isChecked())
{
this->m_FOVRefOrientation = fovTransform->GetMatrix();
this->UpdateOrientationMatrixWidget();
}
}
}
void QmitkMatchPointRegistrationVisualizer::UpdateOrientationMatrixWidget()
{
for (unsigned int r = 0; r < 3; ++r)
{
for (unsigned int c = 0; c < 3; ++c)
{
this->m_Controls->m_tableOrientation->item(r,
c)->setText(QString::number(this->m_FOVRefOrientation.GetVnlMatrix().get(r, c)));
}
}
}
void QmitkMatchPointRegistrationVisualizer::InitRegNode()
{
if (this->m_spSelectedRegNode.IsNotNull())
{
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisGrid, mitk::BoolProperty::New(true));
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisGlyph, mitk::BoolProperty::New(false));
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisPoints, mitk::BoolProperty::New(false));
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisDirection,
mitk::RegVisDirectionProperty::New());
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColorStyle,
mitk::RegVisColorStyleProperty::New(1));
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColorUni, mitk::ColorProperty::New(0,
0.5, 0));
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisGridFrequence,
mitk::IntProperty::New(3));
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisGridShowStart,
mitk::BoolProperty::New(false));
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisGridStartColor,
mitk::ColorProperty::New(0.5, 0, 0));
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisFOVSize,
mitk::Vector3DProperty::New(mitk::Vector3D(100.0)));
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisFOVSpacing,
mitk::Vector3DProperty::New(mitk::Vector3D(5.0)));
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor1Value, mitk::ColorProperty::New(0,
0, 0.5));
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor2Value, mitk::ColorProperty::New(0,
0.7, 0));
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor2Magnitude,
mitk::DoubleProperty::New(1));
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor3Value, mitk::ColorProperty::New(1,
1, 0));
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor3Magnitude,
mitk::DoubleProperty::New(5));
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor4Value, mitk::ColorProperty::New(1,
0, 0));
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor4Magnitude,
mitk::DoubleProperty::New(15));
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColorInterpolate,
mitk::BoolProperty::New(true));
mitk::Point3D origin;
origin.Fill(0.0);
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisFOVOrigin,
mitk::Point3dProperty::New(mitk::Point3D(origin)));
mitk::Vector3D vec(0.0);
vec.SetElement(0, 1.0);
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisFOVOrientation1,
mitk::Vector3DProperty::New(vec));
vec.Fill(0.0);
vec.SetElement(1, 1.0);
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisFOVOrientation2,
mitk::Vector3DProperty::New(vec));
vec.Fill(0.0);
vec.SetElement(2, 1.0);
this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisFOVOrientation3,
mitk::Vector3DProperty::New(vec));
}
}
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/files.cmake b/Plugins/org.mitk.gui.qt.multilabelsegmentation/files.cmake
index 7906c490d9..a2f14a89c5 100644
--- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/files.cmake
+++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/files.cmake
@@ -1,106 +1,95 @@
set(SRC_CPP_FILES
- QmitkMultiLabelSegmentationPreferencePage.cpp
)
set(INTERNAL_CPP_FILES
mitkPluginActivator.cpp
- QmitkMultiLabelSegmentationView.cpp
QmitkThresholdAction.cpp
QmitkConvertSurfaceToLabelAction.cpp
QmitkConvertMaskToLabelAction.cpp
QmitkConvertToMultiLabelSegmentationAction.cpp
QmitkCreateMultiLabelSegmentationAction.cpp
- QmitkLoadMultiLabelPresetAction.cpp
- QmitkCreateMultiLabelPresetAction.cpp
Common/QmitkDataSelectionWidget.cpp
SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.cpp
SegmentationUtilities/QmitkSegmentationUtilityWidget.cpp
SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidget.cpp
SegmentationUtilities/ContourModelToImage/QmitkContourModelToImageWidget.cpp
SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidget.cpp
SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.cpp
SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp
SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidget.cpp
)
set(UI_FILES
- src/internal/QmitkMultiLabelSegmentationControls.ui
src/internal/Common/QmitkDataSelectionWidgetControls.ui
src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesViewControls.ui
src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidgetControls.ui
src/internal/SegmentationUtilities/ContourModelToImage/QmitkContourModelToImageWidgetControls.ui
src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidgetControls.ui
src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidgetControls.ui
src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui
src/internal/SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidgetControls.ui
)
set(MOC_H_FILES
- src/QmitkMultiLabelSegmentationPreferencePage.h
src/internal/mitkPluginActivator.h
- src/internal/QmitkMultiLabelSegmentationView.h
src/internal/QmitkThresholdAction.h
src/internal/QmitkConvertSurfaceToLabelAction.h
- src/internal/QmitkLoadMultiLabelPresetAction.h
- src/internal/QmitkCreateMultiLabelPresetAction.h
src/internal/QmitkConvertMaskToLabelAction.h
src/internal/QmitkConvertToMultiLabelSegmentationAction.h
src/internal/QmitkCreateMultiLabelSegmentationAction.h
src/internal/Common/QmitkDataSelectionWidget.h
src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.h
src/internal/SegmentationUtilities/QmitkSegmentationUtilityWidget.h
src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidget.h
src/internal/SegmentationUtilities/ContourModelToImage/QmitkContourModelToImageWidget.h
src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidget.h
src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.h
src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.h
src/internal/SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidget.h
)
set(CACHED_RESOURCE_FILES
resources/BooleanDifference_48x48.png
resources/BooleanIntersection_48x48.png
resources/BooleanOperations_48x48.png
resources/BooleanUnion_48x48.png
resources/Closing_48x48.png
resources/CTKWidgets_48x48.png
resources/deformablePlane.png
resources/Dilate_48x48.png
resources/Erode_48x48.png
resources/FillHoles_48x48.png
resources/Icons.svg
resources/ImageMasking_48x48.png
resources/MorphologicalOperations_48x48.png
resources/multilabelsegmentation.svg
resources/multilabelsegmentation_utilities.svg
- resources/NewLabel_48x48.png
- resources/NewSegmentationSession_48x48.png
resources/Opening_48x48.png
resources/SurfaceToImage_48x48.png
plugin.xml
)
set(QRC_FILES
resources/multilabelsegmentation.qrc
resources/MultiLabelSegmentationUtilities.qrc
resources/MorphologicalOperationsWidget.qrc
resources/BooleanOperationsWidget.qrc
)
set(CPP_FILES)
foreach(file ${SRC_CPP_FILES})
set(CPP_FILES ${CPP_FILES} src/${file})
endforeach(file ${SRC_CPP_FILES})
#usFunctionEmbedResources(
#CPP_FILES
# LIBRARY_NAME "liborg_mitk_gui_qt_multilabelsegmentation"
#ROOT_DIR resources
#FILES Interactions/SegmentationInteraction.xml
# Interactions/ConfigSegmentation.xml
#)
foreach(file ${INTERNAL_CPP_FILES})
set(CPP_FILES ${CPP_FILES} src/internal/${file})
endforeach(file ${INTERNAL_CPP_FILES})
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/plugin.xml b/Plugins/org.mitk.gui.qt.multilabelsegmentation/plugin.xml
index 7f3d3a7c9a..183ccc3b53 100644
--- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/plugin.xml
+++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/plugin.xml
@@ -1,39 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<plugin>
<extension point="org.blueberry.ui.views">
- <view id="org.mitk.views.multilabelsegmentation"
- name="MultiLabel Segmentation"
- category="Segmentation"
- icon="resources/multilabelsegmentation.svg"
- class="QmitkMultiLabelSegmentationView" />
-
<view id="org.mitk.views.multilabelsegmentationutilities"
name="MultiLabel Segmentation Utilities"
category="Segmentation"
class="QmitkMultiLabelSegmentationUtilitiesView"
icon="resources/multilabelsegmentation_utilities.svg" />
-
- </extension>
-
- <extension point="org.blueberry.ui.preferencePages">
- <page id="org.mitk.gui.qt.application.MultiLabelSegmentationPreferencePage" name="MultiLabel Segmentation" class="QmitkMultiLabelSegmentationPreferencePage">
- <keywordreference id="org.mitk.gui.qt.application.MultiLabelSegmentationPreferencePageKeywords"></keywordreference>
- </page>
- </extension>
-
- <extension point="org.blueberry.ui.keywords">
- <keyword id="org.mitk.gui.qt.application.MultiLabelSegmentationPreferencePageKeywords" label="multi segmentation label multilabel multilabelsegmentation 2d display 3d outline draw transparent overlay show volume rendering data node selection mode enable auto-selection mode smoothed surface creation smoothing value decimation rate"></keyword>
</extension>
<extension point="org.mitk.gui.qt.datamanager.contextMenuActions">
<contextMenuAction nodeDescriptorName="ImageMask" label="Convert to Label" icon="" class="QmitkConvertMaskToLabelAction" />
<contextMenuAction nodeDescriptorName="Surface" label="Convert to Label" icon="" class="QmitkConvertSurfaceToLabelAction" />
<contextMenuAction nodeDescriptorName="Image" label="Create Segmentation" icon="" class="QmitkCreateMultiLabelSegmentationAction" />
<contextMenuAction nodeDescriptorName="Image" label="Convert to Segmentation" icon="" class="QmitkConvertToMultiLabelSegmentationAction" />
- <contextMenuAction nodeDescriptorName="LabelSetImage" label="Save LabelSet Preset" icon="" class="QmitkCreateMultiLabelPresetAction" />
- <contextMenuAction nodeDescriptorName="LabelSetImage" label="Load LabelSet Preset" icon="" class="QmitkLoadMultiLabelPresetAction" />
</extension>
+
</plugin>
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/NewLabel_48x48.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/NewLabel_48x48.png
deleted file mode 100644
index defcc15ebc..0000000000
Binary files a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/NewLabel_48x48.png and /dev/null differ
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/multilabelsegmentation.qrc b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/multilabelsegmentation.qrc
index bc38736d14..2e294d12aa 100644
--- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/multilabelsegmentation.qrc
+++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/multilabelsegmentation.qrc
@@ -1,7 +1,5 @@
<RCC>
<qresource prefix="/multilabelsegmentation" >
<file>multilabelsegmentation.svg</file>
- <file>NewLabel_48x48.png</file>
- <file>NewSegmentationSession_48x48.png</file>
</qresource>
</RCC>
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/QmitkMultiLabelSegmentationPreferencePage.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/QmitkMultiLabelSegmentationPreferencePage.cpp
deleted file mode 100644
index e384407c7c..0000000000
--- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/QmitkMultiLabelSegmentationPreferencePage.cpp
+++ /dev/null
@@ -1,156 +0,0 @@
-/*============================================================================
-
-The Medical Imaging Interaction Toolkit (MITK)
-
-Copyright (c) German Cancer Research Center (DKFZ)
-All rights reserved.
-
-Use of this source code is governed by a 3-clause BSD license that can be
-found in the LICENSE file.
-
-============================================================================*/
-
-#include "QmitkMultiLabelSegmentationPreferencePage.h"
-
-#include <QLabel>
-#include <QPushButton>
-#include <QFormLayout>
-#include <QCheckBox>
-#include <QGroupBox>
-#include <QRadioButton>
-#include <QMessageBox>
-#include <QDoubleSpinBox>
-
-#include <berryIPreferencesService.h>
-#include <berryPlatform.h>
-
-QmitkMultiLabelSegmentationPreferencePage::QmitkMultiLabelSegmentationPreferencePage()
- : m_MainControl(nullptr),
- m_SlimViewCheckBox(nullptr),
- m_RadioOutline(nullptr),
- m_RadioOverlay(nullptr),
- m_SelectionModeCheckBox(nullptr),
- m_SmoothingSpinBox(nullptr),
- m_DecimationSpinBox(nullptr),
- m_Initializing(false)
-{
-
-}
-
-QmitkMultiLabelSegmentationPreferencePage::~QmitkMultiLabelSegmentationPreferencePage()
-{
-
-}
-
-void QmitkMultiLabelSegmentationPreferencePage::Init(berry::IWorkbench::Pointer )
-{
-
-}
-
-void QmitkMultiLabelSegmentationPreferencePage::CreateQtControl(QWidget* parent)
-{
- m_Initializing = true;
- berry::IPreferencesService* prefService = berry::Platform::GetPreferencesService();
-
- m_SegmentationPreferencesNode = prefService->GetSystemPreferences()->Node("/org.mitk.views.multilabelsegmentation");
-
- m_MainControl = new QWidget(parent);
-
- QFormLayout *formLayout = new QFormLayout;
- formLayout->setHorizontalSpacing(8);
- formLayout->setVerticalSpacing(24);
-
- m_SlimViewCheckBox = new QCheckBox("Hide tool button texts and increase icon size", m_MainControl);
- formLayout->addRow("Slim view", m_SlimViewCheckBox);
-
- QVBoxLayout* displayOptionsLayout = new QVBoxLayout;
- m_RadioOutline = new QRadioButton( "Draw as outline", m_MainControl);
- displayOptionsLayout->addWidget( m_RadioOutline );
- m_RadioOverlay = new QRadioButton( "Draw as transparent overlay", m_MainControl);
- displayOptionsLayout->addWidget( m_RadioOverlay );
- formLayout->addRow( "2D display", displayOptionsLayout );
-
- m_SelectionModeCheckBox = new QCheckBox("Enable auto-selection mode", m_MainControl);
- m_SelectionModeCheckBox->setToolTip("If checked the segmentation plugin ensures that only one segmentation and the according greyvalue image are visible at one time.");
- formLayout->addRow("Data node selection mode", m_SelectionModeCheckBox);
-
- QFormLayout* surfaceLayout = new QFormLayout;
- surfaceLayout->setSpacing(8);
-
- m_SmoothingSpinBox = new QDoubleSpinBox(m_MainControl);
- m_SmoothingSpinBox->setMinimum(0.0);
- m_SmoothingSpinBox->setSingleStep(0.5);
- m_SmoothingSpinBox->setValue(0.1);
- m_SmoothingSpinBox->setToolTip("The Smoothing value is used as Sigma for a gaussian blur.");
- surfaceLayout->addRow("Smoothing value (mm)", m_SmoothingSpinBox);
-
- m_DecimationSpinBox = new QDoubleSpinBox(m_MainControl);
- m_DecimationSpinBox->setMinimum(0.0);
- m_DecimationSpinBox->setMaximum(0.99);
- m_DecimationSpinBox->setSingleStep(0.1);
- m_DecimationSpinBox->setValue(0.5);
- m_DecimationSpinBox->setToolTip("Valid range is [0, 1). High values increase decimation, especially when very close to 1. A value of 0 disables decimation.");
- surfaceLayout->addRow("Decimation rate", m_DecimationSpinBox);
-
- formLayout->addRow("Smoothed surface creation", surfaceLayout);
-
- m_MainControl->setLayout(formLayout);
- this->Update();
- m_Initializing = false;
-}
-
-QWidget* QmitkMultiLabelSegmentationPreferencePage::GetQtControl() const
-{
- return m_MainControl;
-}
-
-bool QmitkMultiLabelSegmentationPreferencePage::PerformOk()
-{
- m_SegmentationPreferencesNode->PutBool("slim view", m_SlimViewCheckBox->isChecked());
- m_SegmentationPreferencesNode->PutBool("draw outline", m_RadioOutline->isChecked());
- m_SegmentationPreferencesNode->PutDouble("smoothing value", m_SmoothingSpinBox->value());
- m_SegmentationPreferencesNode->PutDouble("decimation rate", m_DecimationSpinBox->value());
- m_SegmentationPreferencesNode->PutBool("auto selection", m_SelectionModeCheckBox->isChecked());
- return true;
-}
-
-void QmitkMultiLabelSegmentationPreferencePage::PerformCancel()
-{
-
-}
-
-void QmitkMultiLabelSegmentationPreferencePage::Update()
-{
- m_SlimViewCheckBox->setChecked(m_SegmentationPreferencesNode->GetBool("slim view", false));
-
- if (m_SegmentationPreferencesNode->GetBool("draw outline", true) )
- {
- m_RadioOutline->setChecked(true);
- }
- else
- {
- m_RadioOverlay->setChecked(true);
- }
-
- m_SelectionModeCheckBox->setChecked(m_SegmentationPreferencesNode->GetBool("auto selection", false));
-
- if (m_SegmentationPreferencesNode->GetBool("smoothing hint", true))
- {
- m_SmoothingSpinBox->setDisabled(true);
- }
- else
- {
- m_SmoothingSpinBox->setEnabled(true);
- }
-
- m_SmoothingSpinBox->setValue(m_SegmentationPreferencesNode->GetDouble("smoothing value", 0.1));
- m_DecimationSpinBox->setValue(m_SegmentationPreferencesNode->GetDouble("decimation rate", 0.5));
-}
-
-void QmitkMultiLabelSegmentationPreferencePage::OnSmoothingCheckboxChecked(int state)
-{
- if (state != Qt::Unchecked)
- m_SmoothingSpinBox->setDisabled(true);
- else
- m_SmoothingSpinBox->setEnabled(true);
-}
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/QmitkMultiLabelSegmentationPreferencePage.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/QmitkMultiLabelSegmentationPreferencePage.h
deleted file mode 100644
index ef52b83c76..0000000000
--- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/QmitkMultiLabelSegmentationPreferencePage.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*============================================================================
-
-The Medical Imaging Interaction Toolkit (MITK)
-
-Copyright (c) German Cancer Research Center (DKFZ)
-All rights reserved.
-
-Use of this source code is governed by a 3-clause BSD license that can be
-found in the LICENSE file.
-
-============================================================================*/
-
-
-#ifndef QmitkMultiLabelSegmentationPreferencePage_h_included
-#define QmitkMultiLabelSegmentationPreferencePage_h_included
-
-#include "berryIQtPreferencePage.h"
-#include "org_mitk_gui_qt_multilabelsegmentation_Export.h"
-#include <berryIPreferences.h>
-
-class QWidget;
-class QCheckBox;
-class QRadioButton;
-class QDoubleSpinBox;
-
-class MITK_QT_SEGMENTATION QmitkMultiLabelSegmentationPreferencePage : public QObject, public berry::IQtPreferencePage
-{
- Q_OBJECT
- Q_INTERFACES(berry::IPreferencePage)
-
-public:
-
- QmitkMultiLabelSegmentationPreferencePage();
- ~QmitkMultiLabelSegmentationPreferencePage() override;
-
- void Init(berry::IWorkbench::Pointer workbench) override;
-
- void CreateQtControl(QWidget* widget) override;
-
- QWidget* GetQtControl() const override;
-
- bool PerformOk() override;
-
- void PerformCancel() override;
-
- void Update() override;
-
-protected slots:
-
- void OnSmoothingCheckboxChecked(int);
-
-protected:
-
- QWidget* m_MainControl;
- QCheckBox* m_SlimViewCheckBox;
- QRadioButton* m_RadioOutline;
- QRadioButton* m_RadioOverlay;
- QCheckBox* m_SelectionModeCheckBox;
- QDoubleSpinBox* m_SmoothingSpinBox;
- QDoubleSpinBox* m_DecimationSpinBox;
-
- bool m_Initializing;
-
- berry::IPreferences::Pointer m_SegmentationPreferencesNode;
-};
-
-#endif /* QMITKDATAMANAGERPREFERENCEPAGE_H_ */
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelSegmentationAction.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelSegmentationAction.cpp
index d476aa0b44..f993c5e3fa 100644
--- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelSegmentationAction.cpp
+++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelSegmentationAction.cpp
@@ -1,120 +1,100 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
+
#include "QmitkCreateMultiLabelSegmentationAction.h"
#include "mitkLabelSetImage.h"
-#include "mitkRenderingManager.h"
+#include "mitkLabelSetImageHelper.h"
-#include "QInputDialog"
#include "QMessageBox"
-#include "QmitkNewSegmentationDialog.h"
-#include "QmitkMultiLabelSegmentationView.h"
-#include "QmitkSegmentationOrganNamesHandling.cpp"
-//needed for qApp
-#include <qcoreapplication.h>
-
QmitkCreateMultiLabelSegmentationAction::QmitkCreateMultiLabelSegmentationAction()
{
}
QmitkCreateMultiLabelSegmentationAction::~QmitkCreateMultiLabelSegmentationAction()
{
}
-void QmitkCreateMultiLabelSegmentationAction::Run( const QList<mitk::DataNode::Pointer> &selectedNodes )
+void QmitkCreateMultiLabelSegmentationAction::Run(const QList<mitk::DataNode::Pointer>& selectedNodes)
{
if (m_DataStorage.IsNull())
{
auto message = tr("Data storage not set.");
MITK_ERROR << message;
- QMessageBox::warning(nullptr, "New Segmentation Session", message);
+ QMessageBox::warning(nullptr, "New segmentation", message);
return;
}
for ( const auto &referenceNode : selectedNodes )
{
- if (referenceNode.IsNotNull())
+ if (referenceNode.IsNull())
{
- mitk::Image* referenceImage = dynamic_cast<mitk::Image*>( referenceNode->GetData() );
- if (nullptr == referenceImage)
- {
- MITK_WARN << "Could not create multi label segmentation for non-image node - skipping action.";
- continue;
- }
-
- QString newName = QString::fromStdString(referenceNode->GetName());
- newName.append("-labels");
-
- bool ok = false;
- newName = QInputDialog::getText(nullptr, "New Segmentation Session", "New name:", QLineEdit::Normal, newName, &ok);
- if(!ok) return;
-
- mitk::LabelSetImage::Pointer workingImage = mitk::LabelSetImage::New();
-
- try
- {
- workingImage->Initialize(referenceImage);
- }
- catch ( mitk::Exception& e )
- {
- MITK_ERROR << "Exception caught: " << e.GetDescription();
- QMessageBox::warning(nullptr, "New Segmentation Session", "Could not create a new segmentation session.");
- return;
- }
-
- mitk::DataNode::Pointer workingNode = mitk::DataNode::New();
- workingNode->SetData(workingImage);
- workingNode->SetName(newName.toStdString());
-
- // set additional image information
- workingImage->GetExteriorLabel()->SetProperty("name.parent",mitk::StringProperty::New(referenceNode->GetName().c_str()));
- workingImage->GetExteriorLabel()->SetProperty("name.image",mitk::StringProperty::New(newName.toStdString().c_str()));
-
- if (!m_DataStorage->Exists(workingNode))
- m_DataStorage->Add(workingNode, referenceNode);
+ continue;
+ }
- QmitkNewSegmentationDialog* dialog = new QmitkNewSegmentationDialog( );
- dialog->SetSuggestionList( mitk::OrganNamesHandling::GetDefaultOrganColorString());
- dialog->setWindowTitle("New Label");
+ mitk::Image* referenceImage = dynamic_cast<mitk::Image*>(referenceNode->GetData());
+ if (nullptr == referenceImage)
+ {
+ MITK_WARN << "Could not create multi label segmentation for non-image node.";
+ continue;
+ }
- int dialogReturnValue = dialog->exec();
+ mitk::DataNode::Pointer newSegmentationNode;
+ try
+ {
+ newSegmentationNode =
+ mitk::LabelSetImageHelper::CreateNewSegmentationNode(referenceNode, referenceImage);
+ }
+ catch (mitk::Exception& e)
+ {
+ MITK_ERROR << "Exception caught: " << e.GetDescription();
+ QMessageBox::warning(nullptr, "New segmentation", "Could not create a new segmentation.");
+ return;
+ }
- if ( dialogReturnValue == QDialog::Rejected ) return;
+ auto newLabelSetImage = dynamic_cast<mitk::LabelSetImage*>(newSegmentationNode->GetData());
+ if (nullptr == newLabelSetImage)
+ {
+ // something went wrong
+ return;
+ }
- QString segName = dialog->GetSegmentationName();
- if(segName.isEmpty()) segName = "Unnamed";
- workingImage->GetActiveLabelSet()->AddLabel(segName.toStdString(), dialog->GetColor());
+ mitk::Label::Pointer newLabel = mitk::LabelSetImageHelper::CreateNewLabel(newLabelSetImage);
+ newLabelSetImage->GetActiveLabelSet()->AddLabel(newLabel);
+ if (!m_DataStorage->Exists(newSegmentationNode))
+ {
+ m_DataStorage->Add(newSegmentationNode, referenceNode);
}
}
}
void QmitkCreateMultiLabelSegmentationAction::SetDataStorage(mitk::DataStorage* dataStorage)
{
m_DataStorage = dataStorage;
}
void QmitkCreateMultiLabelSegmentationAction::SetFunctionality(berry::QtViewPart*)
{
//not needed
}
void QmitkCreateMultiLabelSegmentationAction::SetSmoothed(bool)
{
//not needed
}
void QmitkCreateMultiLabelSegmentationAction::SetDecimated(bool)
{
//not needed
}
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationControls.ui b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationControls.ui
deleted file mode 100644
index 1b1ea78f8e..0000000000
--- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationControls.ui
+++ /dev/null
@@ -1,786 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>QmitkMultiLabelSegmentationControls</class>
- <widget class="QWidget" name="QmitkMultiLabelSegmentationControls">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>459</width>
- <height>844</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="windowTitle">
- <string>QmitkMultiLabelSegmentation</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_5">
- <item>
- <widget class="QGroupBox" name="groupBox_DataSelection">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="title">
- <string>Data Selection</string>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0">
- <widget class="QLabel" name="label_PatientImage">
- <property name="text">
- <string>Selected Image</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QmitkSingleNodeSelectionWidget" name="m_ReferenceNodeSelector" native="true">
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>40</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="0" column="2" rowspan="2">
- <widget class="QToolButton" name="m_pbNewSegmentationSession">
- <property name="toolTip">
- <string>Create a new segmentation session</string>
- </property>
- <property name="text">
- <string>...</string>
- </property>
- <property name="icon">
- <iconset resource="../../resources/multilabelsegmentation.qrc">
- <normaloff>:/multilabelsegmentation/NewSegmentationSession_48x48.png</normaloff>:/multilabelsegmentation/NewSegmentationSession_48x48.png</iconset>
- </property>
- <property name="iconSize">
- <size>
- <width>28</width>
- <height>28</height>
- </size>
- </property>
- <property name="shortcut">
- <string>N</string>
- </property>
- <property name="autoRaise">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_Segmentation">
- <property name="text">
- <string>Segmentation</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QmitkSingleNodeSelectionWidget" name="m_WorkingNodeSelector" native="true">
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>40</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="2" column="0" colspan="3">
- <widget class="QLabel" name="lblSegmentationWarnings">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="groupBox_Layer">
- <property name="title">
- <string>Layers</string>
- </property>
- <property name="flat">
- <bool>true</bool>
- </property>
- <property name="checkable">
- <bool>false</bool>
- </property>
- <layout class="QHBoxLayout" name="horizontalLayout">
- <item>
- <widget class="QToolButton" name="m_btAddLayer">
- <property name="toolTip">
- <string>Add a layer to the current segmentation session</string>
- </property>
- <property name="text">
- <string>...</string>
- </property>
- <property name="icon">
- <iconset resource="../../../../Modules/SegmentationUI/resources/SegmentationUI.qrc">
- <normaloff>:/Qmitk/AddLayer_48x48.png</normaloff>:/Qmitk/AddLayer_48x48.png</iconset>
- </property>
- <property name="iconSize">
- <size>
- <width>28</width>
- <height>28</height>
- </size>
- </property>
- <property name="autoRaise">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QToolButton" name="m_btDeleteLayer">
- <property name="toolTip">
- <string>Delete the active layer</string>
- </property>
- <property name="text">
- <string>...</string>
- </property>
- <property name="icon">
- <iconset resource="../../../../Modules/SegmentationUI/resources/SegmentationUI.qrc">
- <normaloff>:/Qmitk/DeleteLayer_48x48.png</normaloff>:/Qmitk/DeleteLayer_48x48.png</iconset>
- </property>
- <property name="iconSize">
- <size>
- <width>28</width>
- <height>28</height>
- </size>
- </property>
- <property name="autoRaise">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer1">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>0</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QToolButton" name="m_btPreviousLayer">
- <property name="toolTip">
- <string>Change to the previous available layer</string>
- </property>
- <property name="text">
- <string>...</string>
- </property>
- <property name="icon">
- <iconset resource="../../../../Modules/SegmentationUI/resources/SegmentationUI.qrc">
- <normaloff>:/Qmitk/PreviousLayer_48x48.png</normaloff>:/Qmitk/PreviousLayer_48x48.png</iconset>
- </property>
- <property name="iconSize">
- <size>
- <width>28</width>
- <height>28</height>
- </size>
- </property>
- <property name="autoRaise">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QToolButton" name="m_btNextLayer">
- <property name="toolTip">
- <string>Change to the next available layer</string>
- </property>
- <property name="text">
- <string>...</string>
- </property>
- <property name="icon">
- <iconset resource="../../../../Modules/SegmentationUI/resources/SegmentationUI.qrc">
- <normaloff>:/Qmitk/NextLayer_48x48.png</normaloff>:/Qmitk/NextLayer_48x48.png</iconset>
- </property>
- <property name="iconSize">
- <size>
- <width>28</width>
- <height>28</height>
- </size>
- </property>
- <property name="autoRaise">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QComboBox" name="m_cbActiveLayer">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>50</width>
- <height>30</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>40</width>
- <height>30</height>
- </size>
- </property>
- <property name="font">
- <font>
- <family>MS Shell Dlg 2</family>
- <pointsize>12</pointsize>
- <weight>50</weight>
- <italic>false</italic>
- <bold>false</bold>
- <underline>false</underline>
- <strikeout>false</strikeout>
- </font>
- </property>
- <property name="toolTip">
- <string>Switch to a layer</string>
- </property>
- <item>
- <property name="text">
- <string>0</string>
- </property>
- </item>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="groupBox_Labels">
- <property name="title">
- <string>Labels</string>
- </property>
- <property name="flat">
- <bool>true</bool>
- </property>
- <layout class="QHBoxLayout" name="horizontalLayout_2">
- <item>
- <widget class="QToolButton" name="m_pbNewLabel">
- <property name="toolTip">
- <string>Add a new label to the current segmentation session</string>
- </property>
- <property name="text">
- <string>...</string>
- </property>
- <property name="icon">
- <iconset resource="../../resources/multilabelsegmentation.qrc">
- <normaloff>:/multilabelsegmentation/NewLabel_48x48.png</normaloff>:/multilabelsegmentation/NewLabel_48x48.png</iconset>
- </property>
- <property name="iconSize">
- <size>
- <width>28</width>
- <height>28</height>
- </size>
- </property>
- <property name="shortcut">
- <string>N</string>
- </property>
- <property name="autoRaise">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QToolButton" name="m_btLockExterior">
- <property name="toolTip">
- <string>Lock/Unlock exterior</string>
- </property>
- <property name="text">
- <string>...</string>
- </property>
- <property name="icon">
- <iconset resource="../../../../Modules/SegmentationUI/resources/SegmentationUI.qrc">
- <normaloff>:/Qmitk/UnlockExterior_48x48.png</normaloff>
- <normalon>:/Qmitk/LockExterior_48x48.png</normalon>:/Qmitk/UnlockExterior_48x48.png</iconset>
- </property>
- <property name="iconSize">
- <size>
- <width>28</width>
- <height>28</height>
- </size>
- </property>
- <property name="checkable">
- <bool>true</bool>
- </property>
- <property name="autoRaise">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QToolButton" name="m_tbSavePreset">
- <property name="toolTip">
- <string>Save LabelSet Preset</string>
- </property>
- <property name="text">
- <string>...</string>
- </property>
- <property name="icon">
- <iconset resource="../../../org.mitk.gui.qt.ext/resources/org_mitk_icons.qrc">
- <normaloff>:/org_mitk_icons/icons/awesome/scalable/actions/document-save.svg</normaloff>:/org_mitk_icons/icons/awesome/scalable/actions/document-save.svg</iconset>
- </property>
- <property name="iconSize">
- <size>
- <width>28</width>
- <height>28</height>
- </size>
- </property>
- <property name="autoRaise">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QToolButton" name="m_tbLoadPreset">
- <property name="toolTip">
- <string>Load LabelSet Preset</string>
- </property>
- <property name="text">
- <string>...</string>
- </property>
- <property name="icon">
- <iconset resource="../../../org.mitk.gui.qt.ext/resources/org_mitk_icons.qrc">
- <normaloff>:/org_mitk_icons/icons/awesome/scalable/actions/document-open.svg</normaloff>:/org_mitk_icons/icons/awesome/scalable/actions/document-open.svg</iconset>
- </property>
- <property name="iconSize">
- <size>
- <width>28</width>
- <height>28</height>
- </size>
- </property>
- <property name="autoRaise">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>0</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QToolButton" name="m_pbShowLabelTable">
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>34</height>
- </size>
- </property>
- <property name="toolTip">
- <string>Show a table with all labels in the current segmentation session</string>
- </property>
- <property name="text">
- <string>&gt;&gt;</string>
- </property>
- <property name="iconSize">
- <size>
- <width>28</width>
- <height>28</height>
- </size>
- </property>
- <property name="checkable">
- <bool>true</bool>
- </property>
- <property name="autoRaise">
- <bool>false</bool>
- </property>
- <property name="arrowType">
- <enum>Qt::NoArrow</enum>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QmitkLabelSetWidget" name="m_LabelSetWidget" native="true">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>20</height>
- </size>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QTabWidget" name="m_tw2DTools">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="styleSheet">
- <string notr="true">QTabWidget::tab-bar { alignment: middle; }</string>
- </property>
- <property name="currentIndex">
- <number>0</number>
- </property>
- <widget class="QWidget" name="m_tb2DTools">
- <attribute name="title">
- <string>2D Tools</string>
- </attribute>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="QmitkToolGUIArea" name="m_ManualToolGUIContainer2D" native="true">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <weight>50</weight>
- <bold>false</bold>
- </font>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QmitkToolSelectionBox" name="m_ManualToolSelectionBox2D" native="true">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <weight>50</weight>
- <bold>false</bold>
- </font>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer_1">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="tab_2">
- <attribute name="title">
- <string>3D Tools</string>
- </attribute>
- <layout class="QVBoxLayout" name="verticalLayout_2">
- <item>
- <widget class="QmitkToolGUIArea" name="m_ManualToolGUIContainer3D" native="true">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <weight>50</weight>
- <bold>false</bold>
- </font>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QmitkToolSelectionBox" name="m_ManualToolSelectionBox3D" native="true">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <weight>50</weight>
- <bold>false</bold>
- </font>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer_2">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="m_gbInterpolation">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="title">
- <string>Interpolation</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_4">
- <property name="spacing">
- <number>2</number>
- </property>
- <property name="sizeConstraint">
- <enum>QLayout::SetMinimumSize</enum>
- </property>
- <property name="leftMargin">
- <number>2</number>
- </property>
- <property name="topMargin">
- <number>2</number>
- </property>
- <property name="rightMargin">
- <number>2</number>
- </property>
- <property name="bottomMargin">
- <number>2</number>
- </property>
- <item>
- <widget class="QComboBox" name="m_cbInterpolation">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <item>
- <property name="text">
- <string>Disabled</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>2D Interpolation</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>3D Interpolation</string>
- </property>
- </item>
- </widget>
- </item>
- <item>
- <widget class="QStackedWidget" name="m_swInterpolation">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="currentIndex">
- <number>1</number>
- </property>
- <widget class="QWidget" name="page_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_6">
- <property name="sizeConstraint">
- <enum>QLayout::SetMinimumSize</enum>
- </property>
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <item>
- <widget class="QmitkSliceBasedInterpolatorWidget" name="m_SliceBasedInterpolatorWidget" native="true">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="page_3">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_3">
- <item>
- <widget class="QmitkSurfaceBasedInterpolatorWidget" name="m_SurfaceBasedInterpolatorWidget" native="true">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>50</height>
- </size>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="page">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- </widget>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- <zorder>m_LabelSetWidget</zorder>
- <zorder>groupBox_DataSelection</zorder>
- <zorder>m_tw2DTools</zorder>
- <zorder>m_gbInterpolation</zorder>
- <zorder>groupBox_Layer</zorder>
- <zorder>groupBox_Labels</zorder>
- </widget>
- <layoutdefault spacing="6" margin="11"/>
- <customwidgets>
- <customwidget>
- <class>QmitkSingleNodeSelectionWidget</class>
- <extends>QWidget</extends>
- <header location="global">QmitkSingleNodeSelectionWidget.h</header>
- <container>1</container>
- </customwidget>
- <customwidget>
- <class>QmitkToolSelectionBox</class>
- <extends>QWidget</extends>
- <header location="global">QmitkToolSelectionBox.h</header>
- </customwidget>
- <customwidget>
- <class>QmitkToolGUIArea</class>
- <extends>QWidget</extends>
- <header location="global">QmitkToolGUIArea.h</header>
- </customwidget>
- <customwidget>
- <class>QmitkLabelSetWidget</class>
- <extends>QWidget</extends>
- <header location="global">Qmitk/QmitkLabelSetWidget.h</header>
- <container>1</container>
- </customwidget>
- <customwidget>
- <class>QmitkSliceBasedInterpolatorWidget</class>
- <extends>QWidget</extends>
- <header location="global">QmitkSliceBasedInterpolatorWidget.h</header>
- <container>1</container>
- </customwidget>
- <customwidget>
- <class>QmitkSurfaceBasedInterpolatorWidget</class>
- <extends>QWidget</extends>
- <header location="global">QmitkSurfaceBasedInterpolatorWidget.h</header>
- <container>1</container>
- </customwidget>
- </customwidgets>
- <includes>
- <include location="global">QmitkToolGUIArea.h</include>
- <include location="global">QmitkToolSelectionBox.h</include>
- </includes>
- <resources>
- <include location="../../../../Modules/SegmentationUI/resources/SegmentationUI.qrc"/>
- <include location="../../../org.mitk.gui.qt.ext/resources/org_mitk_icons.qrc"/>
- <include location="../../resources/multilabelsegmentation.qrc"/>
- <include location="../../../../Modules/SegmentationUI/resources/SegmentationUI.qrc"/>
- <include location="../../../org.mitk.gui.qt.ext/resources/org_mitk_icons.qrc"/>
- <include location="../../resources/multilabelsegmentation.qrc"/>
- </resources>
- <connections/>
-</ui>
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.cpp
deleted file mode 100644
index 762e8e5dee..0000000000
--- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.cpp
+++ /dev/null
@@ -1,1168 +0,0 @@
-/*============================================================================
-
-The Medical Imaging Interaction Toolkit (MITK)
-
-Copyright (c) German Cancer Research Center (DKFZ)
-All rights reserved.
-
-Use of this source code is governed by a 3-clause BSD license that can be
-found in the LICENSE file.
-
-============================================================================*/
-
-#include "QmitkMultiLabelSegmentationView.h"
-#include "mitkPluginActivator.h"
-
-// blueberry
-#include <berryIWorkbenchPage.h>
-
-// mitk
-#include <mitkApplicationCursor.h>
-#include <mitkInteractionEventObserver.h>
-#include <mitkImageTimeSelector.h>
-#include <mitkLabelSetImage.h>
-#include <mitkNodePredicateSubGeometry.h>
-#include <mitkPlanePositionManager.h>
-#include <mitkStatusBar.h>
-#include <mitkToolManagerProvider.h>
-
-// Qmitk
-#include "QmitkCreateMultiLabelPresetAction.h"
-#include "QmitkLoadMultiLabelPresetAction.h"
-#include <QmitkNewSegmentationDialog.h>
-#include <QmitkRenderWindow.h>
-#include <QmitkSegmentationOrganNamesHandling.cpp>
-#include <QmitkStyleManager.h>
-
-// us
-#include <usGetModuleContext.h>
-#include <usModule.h>
-#include <usModuleContext.h>
-#include <usModuleResource.h>
-#include <usModuleResourceStream.h>
-
-// Qt
-#include <QInputDialog>
-#include <QMessageBox>
-#include <QShortcut>
-
-#include <itksys/SystemTools.hxx>
-
-#include <regex>
-
-const std::string QmitkMultiLabelSegmentationView::VIEW_ID = "org.mitk.views.multilabelsegmentation";
-
-QmitkMultiLabelSegmentationView::QmitkMultiLabelSegmentationView()
- : m_Parent(nullptr),
- m_RenderWindowPart(nullptr),
- m_ToolManager(nullptr),
- m_ReferenceNode(nullptr),
- m_WorkingNode(nullptr),
- m_AutoSelectionEnabled(false),
- m_MouseCursorSet(false)
-{
-
- auto isImage = mitk::TNodePredicateDataType<mitk::Image>::New();
- auto isDwi = mitk::NodePredicateDataType::New("DiffusionImage");
- auto isDti = mitk::NodePredicateDataType::New("TensorImage");
- auto isOdf = mitk::NodePredicateDataType::New("OdfImage");
- auto isSegment = mitk::NodePredicateDataType::New("Segment");
-
- auto validImages = mitk::NodePredicateOr::New();
- validImages->AddPredicate(mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateNot::New(isSegment)));
- validImages->AddPredicate(isDwi);
- validImages->AddPredicate(isDti);
- validImages->AddPredicate(isOdf);
-
- auto isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true));
- auto isMask = mitk::NodePredicateAnd::New(isBinary, isImage);
-
- auto validSegmentations = mitk::NodePredicateOr::New();
- validSegmentations->AddPredicate(mitk::TNodePredicateDataType<mitk::LabelSetImage>::New());
- validSegmentations->AddPredicate(isMask);
-
- m_SegmentationPredicate = mitk::NodePredicateAnd::New();
- m_SegmentationPredicate->AddPredicate(validSegmentations);
- m_SegmentationPredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")));
- m_SegmentationPredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("hidden object")));
-
- m_ReferencePredicate = mitk::NodePredicateAnd::New();
- m_ReferencePredicate->AddPredicate(validImages);
- m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(m_SegmentationPredicate));
- m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")));
- m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("hidden object")));
-}
-
-QmitkMultiLabelSegmentationView::~QmitkMultiLabelSegmentationView()
-{
- OnLooseLabelSetConnection();
-
- // removing all observers
- for (NodeTagMapType::iterator dataIter = m_WorkingDataObserverTags.begin(); dataIter != m_WorkingDataObserverTags.end(); ++dataIter)
- {
- (*dataIter).first->GetProperty("visible")->RemoveObserver((*dataIter).second);
- }
- m_WorkingDataObserverTags.clear();
-
- mitk::RenderingManager::GetInstance()->RemoveObserver(m_RenderingManagerObserverTag);
-
- m_ToolManager->SetReferenceData(nullptr);
- m_ToolManager->SetWorkingData(nullptr);
-}
-
-/**********************************************************************/
-/* private Q_SLOTS */
-/**********************************************************************/
-void QmitkMultiLabelSegmentationView::OnReferenceSelectionChanged(QList<mitk::DataNode::Pointer> nodes)
-{
- m_ToolManager->ActivateTool(-1);
-
- if (nodes.empty())
- {
- m_Controls.m_WorkingNodeSelector->SetNodePredicate(m_SegmentationPredicate);
- m_ReferenceNode = nullptr;
- m_ToolManager->SetReferenceData(m_ReferenceNode);
- this->UpdateGUI();
- return;
- }
-
- m_ReferenceNode = nodes.first();
- m_ToolManager->SetReferenceData(m_ReferenceNode);
-
- if (m_ReferenceNode.IsNotNull())
- {
- // set a predicate such that a segmentation fits the selected reference image geometry
- auto segPredicate = mitk::NodePredicateAnd::New(m_SegmentationPredicate.GetPointer(),
- mitk::NodePredicateSubGeometry::New(m_ReferenceNode->GetData()->GetGeometry()));
-
- m_Controls.m_WorkingNodeSelector->SetNodePredicate(segPredicate);
-
- if (m_AutoSelectionEnabled)
- {
- // hide all image nodes to later show only the automatically selected ones
- mitk::DataStorage::SetOfObjects::ConstPointer imageNodes =
- this->GetDataStorage()->GetSubset(m_ReferencePredicate);
- for (mitk::DataStorage::SetOfObjects::const_iterator iter = imageNodes->begin(); iter != imageNodes->end(); ++iter)
- {
- (*iter)->SetVisibility(false);
- }
- }
- m_ReferenceNode->SetVisibility(true);
- }
-
- this->UpdateGUI();
-}
-
-void QmitkMultiLabelSegmentationView::OnSegmentationSelectionChanged(QList<mitk::DataNode::Pointer> nodes)
-{
- m_ToolManager->ActivateTool(-1);
-
- // Remove observer if one was registered
- auto finding = m_WorkingDataObserverTags.find(m_WorkingNode);
- if (finding != m_WorkingDataObserverTags.end())
- {
- m_WorkingNode->GetProperty("visible")->RemoveObserver(m_WorkingDataObserverTags[m_WorkingNode]);
- m_WorkingDataObserverTags.erase(m_WorkingNode);
- }
-
- if (nodes.empty())
- {
- m_WorkingNode = nullptr;
- m_ToolManager->SetWorkingData(m_WorkingNode);
- this->UpdateGUI();
- return;
- }
-
- if (m_ReferenceNode.IsNull())
- {
- this->UpdateGUI();
- return;
- }
-
- mitk::Image::ConstPointer referenceImage = dynamic_cast<mitk::Image *>(m_ReferenceNode->GetData());
- if (referenceImage.IsNull())
- {
- this->UpdateGUI();
- return;
- }
-
- if (m_WorkingNode.IsNotNull())
- {
- this->OnLooseLabelSetConnection();
- }
-
- m_WorkingNode = nodes.first();
- m_ToolManager->SetWorkingData(m_WorkingNode);
-
- if (m_WorkingNode.IsNotNull())
- {
- if (m_AutoSelectionEnabled)
- {
- // hide all segmentation nodes to later show only the automatically selected ones
- mitk::DataStorage::SetOfObjects::ConstPointer segmentationNodes =
- this->GetDataStorage()->GetSubset(m_SegmentationPredicate);
- for (mitk::DataStorage::SetOfObjects::const_iterator iter = segmentationNodes->begin(); iter != segmentationNodes->end(); ++iter)
- {
- (*iter)->SetVisibility(false);
- }
- }
- m_WorkingNode->SetVisibility(true);
-
- this->OnEstablishLabelSetConnection();
- m_Controls.m_LabelSetWidget->ResetAllTableWidgetItems();
-
- auto command = itk::SimpleMemberCommand<QmitkMultiLabelSegmentationView>::New();
- command->SetCallbackFunction(this, &QmitkMultiLabelSegmentationView::ValidateSelectionInput);
- m_WorkingDataObserverTags.insert(std::pair<mitk::DataNode *, unsigned long>(m_WorkingNode,
- m_WorkingNode->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command)));
-
- this->InitializeRenderWindows(referenceImage->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, false);
- }
-
- this->UpdateGUI();
-}
-
-void QmitkMultiLabelSegmentationView::OnVisibilityShortcutActivated()
-{
- mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0);
- assert(workingNode);
-
- bool isVisible = false;
- workingNode->GetBoolProperty("visible", isVisible);
- workingNode->SetVisibility(!isVisible);
-
- mitk::RenderingManager::GetInstance()->RequestUpdateAll();
-}
-
-void QmitkMultiLabelSegmentationView::OnLabelToggleShortcutActivated()
-{
- mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0);
- assert(workingNode);
-
- auto workingImage = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
- if (nullptr == workingImage)
- {
- return;
- }
-
- WaitCursorOn();
- workingImage->GetActiveLabelSet()->SetNextActiveLabel();
- workingImage->Modified();
- WaitCursorOff();
-
- mitk::RenderingManager::GetInstance()->RequestUpdateAll();
-}
-
-void QmitkMultiLabelSegmentationView::OnManualTool2DSelected(int id)
-{
- this->ResetMouseCursor();
- mitk::StatusBar::GetInstance()->DisplayText("");
-
- if (id >= 0)
- {
- std::string text = "Active Tool: \"";
- text += m_ToolManager->GetToolById(id)->GetName();
- text += "\"";
- mitk::StatusBar::GetInstance()->DisplayText(text.c_str());
-
- us::ModuleResource resource = m_ToolManager->GetToolById(id)->GetCursorIconResource();
- this->SetMouseCursor(resource, 0, 0);
- }
-}
-
-void QmitkMultiLabelSegmentationView::OnNewLabel()
-{
- m_ToolManager->ActivateTool(-1);
-
- if (m_ReferenceNode.IsNull())
- {
- QMessageBox::information(
- m_Parent, "New Segmentation", "Please load and select an image before starting some action.");
- return;
- }
-
- mitk::Image::ConstPointer referenceImage = dynamic_cast<mitk::Image *>(m_ReferenceNode->GetData());
- if (referenceImage.IsNull())
- {
- QMessageBox::information(
- m_Parent, "New segmentation", "Reference data needs to be an image in order to create a new segmentation.");
- return;
- }
-
- mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0);
- if (!workingNode)
- {
- QMessageBox::information(
- m_Parent, "New segmentation", "Please load and select a multilabel segmentation before starting some action.");
- return;
- }
-
- mitk::LabelSetImage* workingImage = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
- if (!workingImage)
- {
- QMessageBox::information(
- m_Parent, "New segmentation", "Please load and select a multilabel segmentation before starting some action.");
- return;
- }
-
- // ask about the name and organ type of the new segmentation
- auto dialog = new QmitkNewSegmentationDialog(m_Parent);
- QStringList organColors = mitk::OrganNamesHandling::GetDefaultOrganColorString();
- dialog->SetSuggestionList(organColors);
- dialog->setWindowTitle("New Label");
-
- int dialogReturnValue = dialog->exec();
- if (dialogReturnValue == QDialog::Rejected)
- {
- return;
- }
-
- QString segName = dialog->GetSegmentationName();
- if (segName.isEmpty())
- {
- segName = "Unnamed";
- }
- workingImage->GetActiveLabelSet()->AddLabel(segName.toStdString(), dialog->GetColor());
-
- UpdateGUI();
- m_Controls.m_LabelSetWidget->ResetAllTableWidgetItems();
-}
-
-void QmitkMultiLabelSegmentationView::OnSavePreset()
-{
- QmitkAbstractNodeSelectionWidget::NodeList nodes;
- nodes.append(m_WorkingNode);
-
- QmitkCreateMultiLabelPresetAction action;
- action.Run(nodes);
-}
-
-void QmitkMultiLabelSegmentationView::OnLoadPreset()
-{
- QmitkAbstractNodeSelectionWidget::NodeList nodes;
- nodes.append(m_WorkingNode);
-
- QmitkLoadMultiLabelPresetAction action;
- action.Run(nodes);
-}
-
-void QmitkMultiLabelSegmentationView::OnShowLabelTable(bool value)
-{
- if (value)
- m_Controls.m_LabelSetWidget->show();
- else
- m_Controls.m_LabelSetWidget->hide();
-}
-
-void QmitkMultiLabelSegmentationView::OnNewSegmentationSession()
-{
- mitk::DataNode::Pointer referenceNode = m_ToolManager->GetReferenceData(0);
- if (referenceNode.IsNull())
- {
- MITK_ERROR << "'Create new segmentation' button should never be clickable unless a reference image is selected.";
- return;
- }
-
- mitk::Image::ConstPointer referenceImage = dynamic_cast<mitk::Image *>(referenceNode->GetData());
- if (referenceImage.IsNull())
- {
- QMessageBox::information(
- m_Parent, "New segmentation", "Please load and select an image before starting some action.");
- return;
- }
-
- if (referenceImage->GetDimension() <= 1)
- {
- QMessageBox::information(m_Parent, "New segmentation", "Segmentation is currently not supported for 2D images");
- return;
- }
-
- const auto currentTimePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint();
- unsigned int imageTimeStep = 0;
- if (referenceImage->GetTimeGeometry()->IsValidTimePoint(currentTimePoint))
- {
- imageTimeStep = referenceImage->GetTimeGeometry()->TimePointToTimeStep(currentTimePoint);
- }
-
- auto segTemplateImage = referenceImage;
- if (referenceImage->GetDimension() > 3)
- {
- auto result = QMessageBox::question(m_Parent,
- tr("Create a static or dynamic segmentation?"),
- tr("The selected image has multiple time steps.\n\nDo you want to create a static "
- "segmentation that is identical for all time steps or do you want to create a "
- "dynamic segmentation to segment individual time steps?"),
- tr("Create static segmentation"), tr("Create dynamic segmentation"),
- QString(), 0, 0);
- if (result == 0)
- {
- auto selector = mitk::ImageTimeSelector::New();
- selector->SetInput(referenceImage);
- selector->SetTimeNr(0);
- selector->Update();
-
- const auto refTimeGeometry = referenceImage->GetTimeGeometry();
- auto newTimeGeometry = mitk::ProportionalTimeGeometry::New();
- newTimeGeometry->SetFirstTimePoint(refTimeGeometry->GetMinimumTimePoint());
- newTimeGeometry->SetStepDuration(refTimeGeometry->GetMaximumTimePoint() - refTimeGeometry->GetMinimumTimePoint());
-
- mitk::Image::Pointer newImage = selector->GetOutput();
- newTimeGeometry->SetTimeStepGeometry(referenceImage->GetGeometry(imageTimeStep), 0);
- newImage->SetTimeGeometry(newTimeGeometry);
- segTemplateImage = newImage;
- }
- }
-
- QString newName = QString::fromStdString(referenceNode->GetName());
- newName.append("-labels");
-
- bool ok = false;
- newName = QInputDialog::getText(m_Parent, "New segmentation", "New name:", QLineEdit::Normal, newName, &ok);
-
- if (!ok)
- {
- return;
- }
- if (newName.isEmpty())
- {
- newName = "Unnamed";
- }
-
- this->WaitCursorOn();
-
- mitk::LabelSetImage::Pointer workingImage = mitk::LabelSetImage::New();
- try
- {
- workingImage->Initialize(segTemplateImage);
- }
- catch (mitk::Exception& e)
- {
- this->WaitCursorOff();
- MITK_ERROR << "Exception caught: " << e.GetDescription();
- QMessageBox::information(m_Parent, tr("New segmentation"), tr("Could not create a new segmentation.\n"));
- return;
- }
-
- this->WaitCursorOff();
-
- mitk::DataNode::Pointer workingNode = mitk::DataNode::New();
- workingNode->SetData(workingImage);
- workingNode->SetName(newName.toStdString());
-
- workingImage->GetExteriorLabel()->SetProperty("name.parent", mitk::StringProperty::New(referenceNode->GetName().c_str()));
- workingImage->GetExteriorLabel()->SetProperty("name.image", mitk::StringProperty::New(newName.toStdString().c_str()));
-
- this->GetDataStorage()->Add(workingNode, referenceNode);
-
- m_Controls.m_WorkingNodeSelector->SetCurrentSelectedNode(workingNode);
-
- OnNewLabel();
-}
-
-void QmitkMultiLabelSegmentationView::OnGoToLabel(const mitk::Point3D& pos)
-{
- if (m_RenderWindowPart)
- m_RenderWindowPart->SetSelectedPosition(pos);
-}
-
-void QmitkMultiLabelSegmentationView::OnResetView()
-{
- if (m_RenderWindowPart)
- m_RenderWindowPart->ForceImmediateUpdate();
-}
-
-void QmitkMultiLabelSegmentationView::OnAddLayer()
-{
- m_ToolManager->ActivateTool(-1);
-
- mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0);
- assert(workingNode);
-
- auto workingImage = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
- if (nullptr == workingImage)
- {
- return;
- }
-
- QString question = "Do you really want to add a layer to the current segmentation session?";
- QMessageBox::StandardButton answerButton = QMessageBox::question(
- m_Controls.m_LabelSetWidget, "Add layer", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
-
- if (answerButton != QMessageBox::Yes) return;
-
- try
- {
- WaitCursorOn();
- workingImage->AddLayer();
- WaitCursorOff();
- }
- catch ( mitk::Exception& e )
- {
- WaitCursorOff();
- MITK_ERROR << "Exception caught: " << e.GetDescription();
- QMessageBox::information(
- m_Controls.m_LabelSetWidget, "Add Layer", "Could not add a new layer. See error log for details.\n");
- return;
- }
-
- OnNewLabel();
-}
-
-void QmitkMultiLabelSegmentationView::OnDeleteLayer()
-{
- m_ToolManager->ActivateTool(-1);
-
- mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0);
- assert(workingNode);
-
- auto workingImage = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
- if (nullptr == workingImage)
- {
- return;
- }
-
- if (workingImage->GetNumberOfLayers() < 2)
- return;
-
- QString question = "Do you really want to delete the current layer?";
-
- QMessageBox::StandardButton answerButton = QMessageBox::question(
- m_Controls.m_LabelSetWidget, "Delete layer", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
-
- if (answerButton != QMessageBox::Yes)
- {
- return;
- }
-
- try
- {
- this->WaitCursorOn();
- workingImage->RemoveLayer();
- this->WaitCursorOff();
- }
- catch (mitk::Exception& e)
- {
- this->WaitCursorOff();
- MITK_ERROR << "Exception caught: " << e.GetDescription();
- QMessageBox::information(m_Controls.m_LabelSetWidget, "Delete Layer",
- "Could not delete the currently active layer. See error log for details.\n");
- return;
- }
-
- UpdateGUI();
- m_Controls.m_LabelSetWidget->ResetAllTableWidgetItems();
-}
-
-void QmitkMultiLabelSegmentationView::OnPreviousLayer()
-{
- m_ToolManager->ActivateTool(-1);
-
- mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0);
- assert(workingNode);
-
- auto workingImage = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
- if (nullptr == workingImage)
- {
- return;
- }
-
- OnChangeLayer(workingImage->GetActiveLayer() - 1);
-}
-
-void QmitkMultiLabelSegmentationView::OnNextLayer()
-{
- m_ToolManager->ActivateTool(-1);
-
- mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0);
- assert(workingNode);
-
- auto workingImage = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
- if (nullptr == workingImage)
- {
- return;
- }
-
- OnChangeLayer(workingImage->GetActiveLayer() + 1);
-}
-
-void QmitkMultiLabelSegmentationView::OnChangeLayer(int layer)
-{
- m_ToolManager->ActivateTool(-1);
-
- mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0);
- assert(workingNode);
-
- auto workingImage = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
- if (nullptr == workingImage)
- {
- return;
- }
-
- this->WaitCursorOn();
- workingImage->SetActiveLayer(layer);
- this->WaitCursorOff();
-
- UpdateGUI();
- m_Controls.m_LabelSetWidget->ResetAllTableWidgetItems();
-}
-
-void QmitkMultiLabelSegmentationView::OnLockExteriorToggled(bool checked)
-{
- mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0);
- assert(workingNode);
-
- auto workingImage = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
- if (nullptr == workingImage)
- {
- return;
- }
-
- workingImage->GetLabel(0)->SetLocked(checked);
-}
-
-void QmitkMultiLabelSegmentationView::OnInterpolationSelectionChanged(int index)
-{
- if (index == 1)
- {
- m_Controls.m_SurfaceBasedInterpolatorWidget->m_Controls.m_btStart->setChecked(false);//OnToggleWidgetActivation(false);
- m_Controls.m_swInterpolation->setCurrentIndex(0);
- m_Controls.m_swInterpolation->show();
- }
- else if (index == 2)
- {
- m_Controls.m_SliceBasedInterpolatorWidget->m_Controls.m_btStart->setChecked(false);
- m_Controls.m_swInterpolation->setCurrentIndex(1);
- m_Controls.m_swInterpolation->show();
- }
- else
- {
- m_Controls.m_SurfaceBasedInterpolatorWidget->m_Controls.m_btStart->setChecked(false);
- m_Controls.m_SliceBasedInterpolatorWidget->m_Controls.m_btStart->setChecked(false);
- m_Controls.m_swInterpolation->setCurrentIndex(2);
- m_Controls.m_swInterpolation->hide();
- }
-}
-
-/**********************************************************************/
-/* private */
-/**********************************************************************/
-void QmitkMultiLabelSegmentationView::CreateQtPartControl(QWidget *parent)
-{
- m_Parent = parent;
-
- m_Controls.setupUi(parent);
-
- m_Controls.m_tbSavePreset->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-save.svg")));
- m_Controls.m_tbLoadPreset->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-open.svg")));
-
- // *------------------------
- // * Shortcuts
- // *------------------------
- QShortcut* visibilityShortcut = new QShortcut(QKeySequence("CTRL+H"), parent);
- connect(visibilityShortcut, &QShortcut::activated, this, &QmitkMultiLabelSegmentationView::OnVisibilityShortcutActivated);
- QShortcut* labelToggleShortcut = new QShortcut(QKeySequence("CTRL+L"), parent);
- connect(labelToggleShortcut, &QShortcut::activated, this, &QmitkMultiLabelSegmentationView::OnLabelToggleShortcutActivated);
-
- // *------------------------
- // * DATA SELECTION WIDGETS
- // *------------------------
-
- m_Controls.m_ReferenceNodeSelector->SetDataStorage(this->GetDataStorage());
- m_Controls.m_ReferenceNodeSelector->SetNodePredicate(m_ReferencePredicate);
- m_Controls.m_ReferenceNodeSelector->SetInvalidInfo("Select an image");
- m_Controls.m_ReferenceNodeSelector->SetPopUpTitel("Select an image");
- m_Controls.m_ReferenceNodeSelector->SetPopUpHint("Select an image that should be used to define the geometry and bounds of the segmentation.");
-
- m_Controls.m_WorkingNodeSelector->SetDataStorage(this->GetDataStorage());
- m_Controls.m_WorkingNodeSelector->SetNodePredicate(m_SegmentationPredicate);
- m_Controls.m_WorkingNodeSelector->SetInvalidInfo("Select a segmentation");
- m_Controls.m_WorkingNodeSelector->SetPopUpTitel("Select a segmentation");
- m_Controls.m_WorkingNodeSelector->SetPopUpHint("Select a segmentation that should be modified. Only segmentation with the same geometry and within the bounds of the reference image are selected.");
-
- connect(m_Controls.m_ReferenceNodeSelector,
- &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged,
- this, &QmitkMultiLabelSegmentationView::OnReferenceSelectionChanged);
- connect(m_Controls.m_WorkingNodeSelector,
- &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged,
- this, &QmitkMultiLabelSegmentationView::OnSegmentationSelectionChanged);
-
- // *------------------------
- // * ToolManager
- // *------------------------
-
- m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(mitk::ToolManagerProvider::MULTILABEL_SEGMENTATION);
- m_ToolManager->SetDataStorage(*(this->GetDataStorage()));
- m_ToolManager->InitializeTools();
- m_Controls.m_ManualToolSelectionBox2D->SetToolManager(*m_ToolManager);
- m_Controls.m_ManualToolSelectionBox3D->SetToolManager(*m_ToolManager);
-
- // *------------------------
- // * LabelSetWidget
- // *------------------------
-
- m_Controls.m_LabelSetWidget->SetDataStorage(this->GetDataStorage());
- m_Controls.m_LabelSetWidget->SetOrganColors(mitk::OrganNamesHandling::GetDefaultOrganColorString());
- m_Controls.m_LabelSetWidget->hide();
-
- // *------------------------
- // * Interpolation
- // *------------------------
-
- m_Controls.m_SurfaceBasedInterpolatorWidget->SetDataStorage(*(this->GetDataStorage()));
- m_Controls.m_SliceBasedInterpolatorWidget->SetDataStorage(*(this->GetDataStorage()));
- connect(m_Controls.m_cbInterpolation, QOverload<int>::of(&QComboBox::activated), this, &QmitkMultiLabelSegmentationView::OnInterpolationSelectionChanged);
-
- m_Controls.m_cbInterpolation->setCurrentIndex(0);
- m_Controls.m_swInterpolation->hide();
-
- m_Controls.m_gbInterpolation->hide(); // See T27436
-
- QString segTools2D = tr("Add Subtract Fill Erase Paint Wipe 'Region Growing' 'Live Wire'");
- QString segTools3D = tr("Threshold");
-
- std::regex extSegTool2DRegEx("SegTool2D$");
- std::regex extSegTool3DRegEx("SegTool3D$");
-
- auto tools = m_ToolManager->GetTools();
- for (const auto &tool : tools)
- {
- if (std::regex_search(tool->GetNameOfClass(), extSegTool2DRegEx))
- {
- segTools2D.append(QString(" '%1'").arg(tool->GetName()));
- }
- else if (std::regex_search(tool->GetNameOfClass(), extSegTool3DRegEx))
- {
- segTools3D.append(QString(" '%1'").arg(tool->GetName()));
- }
- }
-
- // *------------------------
- // * ToolSelection 2D
- // *------------------------
-
- m_Controls.m_ManualToolSelectionBox2D->SetGenerateAccelerators(true);
- m_Controls.m_ManualToolSelectionBox2D->SetToolGUIArea(m_Controls.m_ManualToolGUIContainer2D);
- m_Controls.m_ManualToolSelectionBox2D->SetDisplayedToolGroups(segTools2D.toStdString());
- m_Controls.m_ManualToolSelectionBox2D->SetLayoutColumns(3);
- m_Controls.m_ManualToolSelectionBox2D->SetEnabledMode(
- QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible);
- connect(m_Controls.m_ManualToolSelectionBox2D, &QmitkToolSelectionBox::ToolSelected,
- this, &QmitkMultiLabelSegmentationView::OnManualTool2DSelected);
-
- // *------------------------
- // * ToolSelection 3D
- // *------------------------
-
- m_Controls.m_ManualToolSelectionBox3D->SetGenerateAccelerators(true);
- m_Controls.m_ManualToolSelectionBox3D->SetToolGUIArea(m_Controls.m_ManualToolGUIContainer3D);
- m_Controls.m_ManualToolSelectionBox3D->SetDisplayedToolGroups(segTools3D.toStdString()); // todo add : FastMarching3D RegionGrowing Watershed
- m_Controls.m_ManualToolSelectionBox3D->SetLayoutColumns(3);
- m_Controls.m_ManualToolSelectionBox3D->SetEnabledMode(
- QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible);
-
- // *------------------------*
- // * Connect Buttons
- // *------------------------*
-
- connect(m_Controls.m_pbNewLabel, &QToolButton::clicked, this, &QmitkMultiLabelSegmentationView::OnNewLabel);
- connect(m_Controls.m_tbSavePreset, &QToolButton::clicked, this, &QmitkMultiLabelSegmentationView::OnSavePreset);
- connect(m_Controls.m_tbLoadPreset, &QToolButton::clicked, this, &QmitkMultiLabelSegmentationView::OnLoadPreset);
- connect(m_Controls.m_pbNewSegmentationSession, &QToolButton::clicked, this, &QmitkMultiLabelSegmentationView::OnNewSegmentationSession);
- connect(m_Controls.m_pbShowLabelTable, &QToolButton::toggled, this, &QmitkMultiLabelSegmentationView::OnShowLabelTable);
-
- // *------------------------*
- // * Connect LabelSetWidget
- // *------------------------*
-
- connect(m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::goToLabel,
- this, &QmitkMultiLabelSegmentationView::OnGoToLabel);
- connect(m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::resetView,
- this, &QmitkMultiLabelSegmentationView::OnResetView);
-
- // *------------------------*
- // * DATA SLECTION WIDGET
- // *------------------------*
-
- connect(m_Controls.m_btAddLayer, &QToolButton::clicked, this, &QmitkMultiLabelSegmentationView::OnAddLayer);
- connect(m_Controls.m_btDeleteLayer, &QToolButton::clicked, this, &QmitkMultiLabelSegmentationView::OnDeleteLayer);
- connect(m_Controls.m_btPreviousLayer, &QToolButton::clicked, this, &QmitkMultiLabelSegmentationView::OnPreviousLayer);
- connect(m_Controls.m_btNextLayer, &QToolButton::clicked, this, &QmitkMultiLabelSegmentationView::OnNextLayer);
- connect(m_Controls.m_btLockExterior, &QToolButton::toggled, this, &QmitkMultiLabelSegmentationView::OnLockExteriorToggled);
- connect(m_Controls.m_cbActiveLayer, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &QmitkMultiLabelSegmentationView::OnChangeLayer);
-
- auto command = itk::SimpleMemberCommand<QmitkMultiLabelSegmentationView>::New();
- command->SetCallbackFunction(this, &QmitkMultiLabelSegmentationView::ValidateSelectionInput);
- m_RenderingManagerObserverTag =
- mitk::RenderingManager::GetInstance()->AddObserver(mitk::RenderingManagerViewsInitializedEvent(), command);
-
- m_RenderWindowPart = this->GetRenderWindowPart();
- if (nullptr != m_RenderWindowPart)
- {
- this->RenderWindowPartActivated(m_RenderWindowPart);
- }
-
- // Make sure the GUI notices if appropriate data is already present on creation.
- // Should be done last, if everything else is configured because it triggers the autoselection of data.
- m_Controls.m_ReferenceNodeSelector->SetAutoSelectNewNodes(true);
- m_Controls.m_WorkingNodeSelector->SetAutoSelectNewNodes(true);
-
- this->UpdateGUI();
-}
-
-void QmitkMultiLabelSegmentationView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart)
-{
- if (m_RenderWindowPart != renderWindowPart)
- {
- m_RenderWindowPart = renderWindowPart;
- }
-
- if (m_Parent)
- {
- m_Parent->setEnabled(true);
- }
-
- // tell the interpolation about tool manager, data storage and render window part
- QList<mitk::SliceNavigationController *> controllers;
- controllers.push_back(m_RenderWindowPart->GetQmitkRenderWindow("axial")->GetSliceNavigationController());
- controllers.push_back(m_RenderWindowPart->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController());
- controllers.push_back(m_RenderWindowPart->GetQmitkRenderWindow("coronal")->GetSliceNavigationController());
- m_Controls.m_SliceBasedInterpolatorWidget->SetSliceNavigationControllers(controllers);
-}
-
-void QmitkMultiLabelSegmentationView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/)
-{
- m_RenderWindowPart = nullptr;
- if (m_Parent)
- {
- m_Parent->setEnabled(false);
- }
-}
-
-void QmitkMultiLabelSegmentationView::OnPreferencesChanged(const berry::IBerryPreferences* prefs)
-{
- bool slimView = prefs->GetBool("slim view", false);
- m_Controls.m_ManualToolSelectionBox2D->SetShowNames(!slimView);
- m_Controls.m_ManualToolSelectionBox3D->SetShowNames(!slimView);
-
- m_AutoSelectionEnabled = prefs->GetBool("auto selection", false);
-
- this->ApplyDisplayOptions();
-}
-
-void QmitkMultiLabelSegmentationView::NodeAdded(const mitk::DataNode* node)
-{
- if (m_SegmentationPredicate->CheckNode(node))
- {
- this->ApplyDisplayOptions(const_cast<mitk::DataNode*>(node));
- }
-}
-
-void QmitkMultiLabelSegmentationView::NodeRemoved(const mitk::DataNode* node)
-{
- if (m_SegmentationPredicate->CheckNode(node))
- {
- // remove all possible contour markers of the segmentation
- mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = this->GetDataStorage()->GetDerivations(
- node, mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true)));
-
- ctkPluginContext *context = mitk::PluginActivator::getContext();
- ctkServiceReference ppmRef = context->getServiceReference<mitk::PlanePositionManagerService>();
- mitk::PlanePositionManagerService *service = context->getService<mitk::PlanePositionManagerService>(ppmRef);
-
- for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it)
- {
- std::string nodeName = node->GetName();
- unsigned int t = nodeName.find_last_of(" ");
- unsigned int id = atof(nodeName.substr(t + 1).c_str()) - 1;
-
- service->RemovePlanePosition(id);
-
- this->GetDataStorage()->Remove(it->Value());
- }
-
- context->ungetService(ppmRef);
- service = nullptr;
- }
-}
-
-void QmitkMultiLabelSegmentationView::ApplyDisplayOptions()
-{
- if (!m_Parent)
- {
- return;
- }
-
- mitk::DataNode::Pointer workingData = m_ToolManager->GetWorkingData(0);
- mitk::DataStorage::SetOfObjects::ConstPointer allImages = this->GetDataStorage()->GetSubset(m_SegmentationPredicate);
- for (mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter)
- {
- this->ApplyDisplayOptions(*iter);
- }
-
- mitk::RenderingManager::GetInstance()->RequestUpdateAll();
-}
-
-void QmitkMultiLabelSegmentationView::ApplyDisplayOptions(mitk::DataNode* node)
-{
- if (nullptr == node)
- {
- return;
- }
-
- auto drawOutline = mitk::BoolProperty::New(GetPreferences()->GetBool("draw outline", true));
- auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(node->GetData());
- if (nullptr != labelSetImage)
- {
- // node is a multi label segmentation
- node->SetProperty("labelset.contour.active", drawOutline);
- // force render window update to show outline
- node->GetData()->Modified();
- }
- else if (nullptr != node->GetData())
- {
- // node is actually a 'single label' segmentation,
- // but its outline property can be set in the 'multi label' segmentation preference page as well
- bool isBinary = false;
- node->GetBoolProperty("binary", isBinary);
- if (isBinary)
- {
- node->SetProperty("outline binary", drawOutline);
- node->SetProperty("outline width", mitk::FloatProperty::New(2.0));
- // force render window update to show outline
- node->GetData()->Modified();
- }
- }
-}
-
-void QmitkMultiLabelSegmentationView::OnEstablishLabelSetConnection()
-{
- if (m_WorkingNode.IsNull())
- {
- return;
- }
-
- auto workingImage = dynamic_cast<mitk::LabelSetImage*>(m_WorkingNode->GetData());
- if (nullptr == workingImage)
- {
- // node is a "single label" / "binary" image --> no label set
- return;
- }
-
- workingImage->GetActiveLabelSet()->AddLabelEvent += mitk::MessageDelegate<QmitkLabelSetWidget>(
- m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems);
- workingImage->GetActiveLabelSet()->RemoveLabelEvent += mitk::MessageDelegate<QmitkLabelSetWidget>(
- m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems);
- workingImage->GetActiveLabelSet()->ModifyLabelEvent += mitk::MessageDelegate<QmitkLabelSetWidget>(
- m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems);
- workingImage->GetActiveLabelSet()->AllLabelsModifiedEvent += mitk::MessageDelegate<QmitkLabelSetWidget>(
- m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems);
- workingImage->GetActiveLabelSet()->ActiveLabelEvent +=
- mitk::MessageDelegate1<QmitkLabelSetWidget, mitk::Label::PixelType>(m_Controls.m_LabelSetWidget,
- &QmitkLabelSetWidget::SelectLabelByPixelValue);
-
- // Removed in T27851 to have a chance to react to AfterChangeLayerEvent. Did it brake something?
- // workingImage->BeforeChangeLayerEvent += mitk::MessageDelegate<QmitkMultiLabelSegmentationView>(
- // this, &QmitkMultiLabelSegmentationView::OnLooseLabelSetConnection);
-
- workingImage->AfterChangeLayerEvent += mitk::MessageDelegate<QmitkMultiLabelSegmentationView>(
- this, &QmitkMultiLabelSegmentationView::UpdateGUI);
-}
-
-void QmitkMultiLabelSegmentationView::OnLooseLabelSetConnection()
-{
- if (m_WorkingNode.IsNull())
- {
- return;
- }
-
- auto workingImage = dynamic_cast<mitk::LabelSetImage*>(m_WorkingNode->GetData());
- if (nullptr == workingImage)
- {
- // data (type) was changed in-place, e.g. LabelSetImage -> (binary) image
- return;
- }
-
- // Reset LabelSetWidget Events
- workingImage->GetActiveLabelSet()->AddLabelEvent -= mitk::MessageDelegate<QmitkLabelSetWidget>(
- m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems);
- workingImage->GetActiveLabelSet()->RemoveLabelEvent -= mitk::MessageDelegate<QmitkLabelSetWidget>(
- m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems);
- workingImage->GetActiveLabelSet()->ModifyLabelEvent -= mitk::MessageDelegate<QmitkLabelSetWidget>(
- m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems);
- workingImage->GetActiveLabelSet()->AllLabelsModifiedEvent -= mitk::MessageDelegate<QmitkLabelSetWidget>(
- m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems);
- workingImage->GetActiveLabelSet()->ActiveLabelEvent -=
- mitk::MessageDelegate1<QmitkLabelSetWidget, mitk::Label::PixelType>(m_Controls.m_LabelSetWidget,
- &QmitkLabelSetWidget::SelectLabelByPixelValue);
-
- // Removed in T27851 to have a chance to react to AfterChangeLayerEvent. Did it brake something?
- // workingImage->BeforeChangeLayerEvent -= mitk::MessageDelegate<QmitkMultiLabelSegmentationView>(
- // this, &QmitkMultiLabelSegmentationView::OnLooseLabelSetConnection);
-
- workingImage->AfterChangeLayerEvent -= mitk::MessageDelegate<QmitkMultiLabelSegmentationView>(
- this, &QmitkMultiLabelSegmentationView::UpdateGUI);
-}
-
-void QmitkMultiLabelSegmentationView::ResetMouseCursor()
-{
- if (m_MouseCursorSet)
- {
- mitk::ApplicationCursor::GetInstance()->PopCursor();
- m_MouseCursorSet = false;
- }
-}
-
-void QmitkMultiLabelSegmentationView::SetMouseCursor(const us::ModuleResource& resource, int hotspotX, int hotspotY)
-{
- // Remove previously set mouse cursor
- if (m_MouseCursorSet)
- this->ResetMouseCursor();
-
- if (resource)
- {
- us::ModuleResourceStream cursor(resource, std::ios::binary);
- mitk::ApplicationCursor::GetInstance()->PushCursor(cursor, hotspotX, hotspotY);
- m_MouseCursorSet = true;
- }
-}
-
-void QmitkMultiLabelSegmentationView::UpdateGUI()
-{
- mitk::DataNode* referenceNode = m_ToolManager->GetReferenceData(0);
- bool hasReferenceNode = referenceNode != nullptr;
-
- mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0);
- bool hasWorkingNode = workingNode != nullptr;
-
- m_Controls.m_pbNewSegmentationSession->setEnabled(false);
- m_Controls.m_gbInterpolation->setEnabled(false);
- m_Controls.m_SliceBasedInterpolatorWidget->setEnabled(false);
- m_Controls.m_SurfaceBasedInterpolatorWidget->setEnabled(false);
- m_Controls.m_btDeleteLayer->setEnabled(false);
- m_Controls.m_cbActiveLayer->setEnabled(false);
- m_Controls.m_btPreviousLayer->setEnabled(false);
- m_Controls.m_btNextLayer->setEnabled(false);
- m_Controls.m_btLockExterior->setChecked(false);
- m_Controls.m_pbShowLabelTable->setChecked(false);
-
- if (hasReferenceNode)
- {
- m_Controls.m_pbNewSegmentationSession->setEnabled(true);
- }
-
- if (hasWorkingNode)
- {
- m_Controls.m_cbActiveLayer->blockSignals(true);
- m_Controls.m_cbActiveLayer->clear();
-
- mitk::LabelSetImage* workingImage = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
- if (nullptr != workingImage)
- {
- int numberOfLayers = workingImage->GetNumberOfLayers();
- for (unsigned int lidx = 0; lidx < workingImage->GetNumberOfLayers(); ++lidx)
- {
- m_Controls.m_cbActiveLayer->addItem(QString::number(lidx));
- }
-
- int activeLayer = workingImage->GetActiveLayer();
- m_Controls.m_cbActiveLayer->setCurrentIndex(activeLayer);
- m_Controls.m_cbActiveLayer->blockSignals(false);
-
- m_Controls.m_btDeleteLayer->setEnabled(numberOfLayers > 1);
- m_Controls.m_cbActiveLayer->setEnabled(numberOfLayers > 1);
- m_Controls.m_btPreviousLayer->setEnabled(activeLayer > 0);
- m_Controls.m_btNextLayer->setEnabled(activeLayer != numberOfLayers - 1);
-
- m_Controls.m_btLockExterior->setChecked(workingImage->GetLabel(0, activeLayer)->GetLocked());
- m_Controls.m_pbShowLabelTable->setChecked(workingImage->GetNumberOfLabels() > 1 /*1st is exterior*/);
- }
- }
-
- if (hasWorkingNode && hasReferenceNode)
- {
- m_Controls.m_gbInterpolation->setEnabled(true);
- m_Controls.m_SliceBasedInterpolatorWidget->setEnabled(true);
- m_Controls.m_SurfaceBasedInterpolatorWidget->setEnabled(true);
-
- int layer = -1;
- referenceNode->GetIntProperty("layer", layer);
- workingNode->SetIntProperty("layer", layer + 1);
- }
-
- this->ValidateSelectionInput();
-}
-
-void QmitkMultiLabelSegmentationView::ValidateSelectionInput()
-{
- this->UpdateWarningLabel("");
-
- m_Controls.groupBox_Layer->setEnabled(false);
- m_Controls.groupBox_Labels->setEnabled(false);
- m_Controls.m_LabelSetWidget->setEnabled(false);
- // the argument is actually not used
- // enable status depends on the tool manager selection
- m_Controls.m_ManualToolSelectionBox2D->setEnabled(false);
- m_Controls.m_ManualToolSelectionBox3D->setEnabled(false);
-
- mitk::DataNode* referenceNode = m_Controls.m_ReferenceNodeSelector->GetSelectedNode();
- mitk::DataNode* workingNode = m_Controls.m_WorkingNodeSelector->GetSelectedNode();
-
- if (nullptr == referenceNode)
- {
- return;
- }
-
- if (nullptr == workingNode)
- {
- return;
- }
-
- mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart();
- auto workingNodeIsVisible = renderWindowPart &&
- workingNode->IsVisible(renderWindowPart->GetQmitkRenderWindow("axial")->GetRenderer());
- if (!workingNodeIsVisible)
- {
- this->UpdateWarningLabel(tr("The selected segmentation is currently not visible!"));
- return;
- }
-
- /*
- * Here we check whether the geometry of the selected segmentation image is aligned with the worldgeometry.
- * At the moment it is not supported to use a geometry different from the selected image for reslicing.
- * For further information see Bug 16063
- */
- const mitk::BaseGeometry *workingNodeGeo = workingNode->GetData()->GetGeometry();
- const mitk::BaseGeometry *worldGeo =
- renderWindowPart->GetQmitkRenderWindow("3d")->GetSliceNavigationController()->GetCurrentGeometry3D();
- if (nullptr != workingNodeGeo && nullptr != worldGeo)
- {
- if (mitk::Equal(*workingNodeGeo->GetBoundingBox(), *worldGeo->GetBoundingBox(), mitk::eps, true))
- {
- m_ToolManager->SetReferenceData(referenceNode);
- m_ToolManager->SetWorkingData(workingNode);
- m_Controls.groupBox_Layer->setEnabled(true);
- m_Controls.groupBox_Labels->setEnabled(true);
- m_Controls.m_LabelSetWidget->setEnabled(true);
- m_Controls.m_ManualToolSelectionBox2D->setEnabled(true);
- m_Controls.m_ManualToolSelectionBox3D->setEnabled(true);
- return;
- }
- }
-
- m_ToolManager->SetReferenceData(referenceNode);
- m_ToolManager->SetWorkingData(nullptr);
- this->UpdateWarningLabel(tr("Please perform a reinit on the segmentation image!"));
-}
-
-void QmitkMultiLabelSegmentationView::UpdateWarningLabel(QString text)
-{
- if (text.size() == 0)
- {
- m_Controls.lblSegmentationWarnings->hide();
- }
- else
- {
- m_Controls.lblSegmentationWarnings->show();
- }
- m_Controls.lblSegmentationWarnings->setText("<font color=\"red\">" + text + "</font>");
-}
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.h
deleted file mode 100644
index e7979bab60..0000000000
--- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.h
+++ /dev/null
@@ -1,171 +0,0 @@
-/*============================================================================
-
-The Medical Imaging Interaction Toolkit (MITK)
-
-Copyright (c) German Cancer Research Center (DKFZ)
-All rights reserved.
-
-Use of this source code is governed by a 3-clause BSD license that can be
-found in the LICENSE file.
-
-============================================================================*/
-
-#ifndef QMITKMULTILABELSEGMENTATIONVIEW_H
-#define QMITKMULTILABELSEGMENTATIONVIEW_H
-
-#include "ui_QmitkMultiLabelSegmentationControls.h"
-
-#include <QmitkAbstractView.h>
-#include <mitkIRenderWindowPartListener.h>
-
-#include <berryIBerryPreferences.h>
-
-/**
-* @brief The multilabel segmentation view provides a set of tool to use different segmentation
-* algorithms.
-* It provides two selection widgets to load an image node and a segmentation node
-* on which to perform the segmentation. Creating new segmentation nodes is also possible.
-* The available segmentation tools are grouped into "2D"- and "3D"-tools.
-*
-* Most segmentation tools / algorithms need some kind of user interaction, where the
-* user is asked to draw something in the image display or set some seed points / start values.
-* The tools also often provide additional propeties so that a user can modify the
-* algorithm's behavior.
-*
-* In contrast to the basic QmitkSegmentationView this class additionally provides options to
-* work with different layers (create new layers, switch between layers).
-* Moreover, a multilabel widget displays all the existing labels of a multilabel segmentation
-* for the currently active layer.
-* The multilabel widget allows to control the labels by creatin new one, removing existing ones,
-* showing / hiding single labels, merging labels, (re-)naming them etc.
-*
-* Interpolation for multilabel segmentations is currently not implemented.
-*/
-class QmitkMultiLabelSegmentationView : public QmitkAbstractView, public mitk::IRenderWindowPartListener
-{
- Q_OBJECT
-
-public:
-
- static const std::string VIEW_ID;
-
- QmitkMultiLabelSegmentationView();
- ~QmitkMultiLabelSegmentationView() override;
-
-private Q_SLOTS:
-
- // reaction to the selection of a new reference image in the selection widget
- void OnReferenceSelectionChanged(QList<mitk::DataNode::Pointer> nodes);
-
- // reaction to the selection of a new segmentation image in the selection widget
- void OnSegmentationSelectionChanged(QList<mitk::DataNode::Pointer> nodes);
-
- // reaction to the shortcut for toggling the visibility of the working node
- void OnVisibilityShortcutActivated();
-
- // reaction to the shortcut for iterating over all labels
- void OnLabelToggleShortcutActivated();
-
- // reaction to the selection of any 2D segmentation tool
- void OnManualTool2DSelected(int id);
-
- // reaction to button "New Label"
- void OnNewLabel();
-
- // reaction to button "Save Preset"
- void OnSavePreset();
-
- // reaction to button "Load Preset"
- void OnLoadPreset();
-
- // reaction to button "Show Label Table"
- void OnShowLabelTable(bool value);
-
- // reaction to button "New Segmentation Session"
- void OnNewSegmentationSession();
-
- // reaction to signal "goToLabel" from labelset widget
- void OnGoToLabel(const mitk::Point3D &pos);
-
- void OnResetView();
-
- // reaction to the button "Add Layer"
- void OnAddLayer();
-
- // reaction to the button "Delete Layer"
- void OnDeleteLayer();
-
- // reaction to the button "Previous Layer"
- void OnPreviousLayer();
-
- // reaction to the button "Next Layer"
- void OnNextLayer();
-
- // reaction to the combobox change "Change Layer"
- void OnChangeLayer(int);
-
- // reaction to the button "Lock exterior"
- void OnLockExteriorToggled(bool);
-
- // reaction to ...
- void OnInterpolationSelectionChanged(int);
-
-private:
-
- void CreateQtPartControl(QWidget *parent) override;
-
- void SetFocus() override {}
-
- void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) override;
- void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) override;
-
- void OnPreferencesChanged(const berry::IBerryPreferences* prefs) override;
-
- void NodeAdded(const mitk::DataNode *node) override;
-
- void NodeRemoved(const mitk::DataNode* node) override;
-
- // make sure all images / segmentations look according to the user preference settings
- void ApplyDisplayOptions();
-
- // decorates a DataNode according to the user preference settings
- void ApplyDisplayOptions(mitk::DataNode* node);
-
- void OnEstablishLabelSetConnection();
-
- void OnLooseLabelSetConnection();
-
- void ResetMouseCursor();
-
- void SetMouseCursor(const us::ModuleResource&, int hotspotX, int hotspotY);
-
- void UpdateGUI();
-
- void ValidateSelectionInput();
-
- void UpdateWarningLabel(QString text);
-
- QWidget *m_Parent;
-
- Ui::QmitkMultiLabelSegmentationControls m_Controls;
-
- mitk::IRenderWindowPart *m_RenderWindowPart;
-
- mitk::ToolManager *m_ToolManager;
-
- mitk::DataNode::Pointer m_ReferenceNode;
- mitk::DataNode::Pointer m_WorkingNode;
-
- typedef std::map<mitk::DataNode *, unsigned long> NodeTagMapType;
- NodeTagMapType m_WorkingDataObserverTags;
- unsigned int m_RenderingManagerObserverTag;
-
- mitk::NodePredicateAnd::Pointer m_ReferencePredicate;
- mitk::NodePredicateAnd::Pointer m_SegmentationPredicate;
-
- bool m_AutoSelectionEnabled;
- bool m_MouseCursorSet;
-
-};
-
-#endif // QMITKMULTILABELSEGMENTATIONVIEW_H
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/mitkPluginActivator.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/mitkPluginActivator.cpp
index ac1102b2a0..b699458957 100644
--- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/mitkPluginActivator.cpp
+++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/mitkPluginActivator.cpp
@@ -1,56 +1,49 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkPluginActivator.h"
-#include "QmitkMultiLabelSegmentationView.h"
#include "QmitkThresholdAction.h"
#include "QmitkConvertSurfaceToLabelAction.h"
#include "QmitkConvertMaskToLabelAction.h"
#include "QmitkConvertToMultiLabelSegmentationAction.h"
#include "QmitkCreateMultiLabelSegmentationAction.h"
-#include "QmitkMultiLabelSegmentationPreferencePage.h"
-#include "QmitkLoadMultiLabelPresetAction.h"
-#include "QmitkCreateMultiLabelPresetAction.h"
+
#include "SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.h"
#include <usModuleInitialization.h>
ctkPluginContext* mitk::PluginActivator::m_Context = nullptr;
//MLI TODO
US_INITIALIZE_MODULE //("MultiLabelSegmentation", "liborg_mitk_gui_qt_multilabelsegmentation")
void mitk::PluginActivator::start(ctkPluginContext *context)
{
- BERRY_REGISTER_EXTENSION_CLASS(QmitkMultiLabelSegmentationView, context)
BERRY_REGISTER_EXTENSION_CLASS(QmitkThresholdAction, context)
BERRY_REGISTER_EXTENSION_CLASS(QmitkConvertSurfaceToLabelAction, context)
BERRY_REGISTER_EXTENSION_CLASS(QmitkConvertMaskToLabelAction, context)
BERRY_REGISTER_EXTENSION_CLASS(QmitkConvertToMultiLabelSegmentationAction, context)
BERRY_REGISTER_EXTENSION_CLASS(QmitkCreateMultiLabelSegmentationAction, context)
- BERRY_REGISTER_EXTENSION_CLASS(QmitkCreateMultiLabelPresetAction, context)
- BERRY_REGISTER_EXTENSION_CLASS(QmitkLoadMultiLabelPresetAction, context)
- BERRY_REGISTER_EXTENSION_CLASS(QmitkMultiLabelSegmentationPreferencePage, context)
BERRY_REGISTER_EXTENSION_CLASS(QmitkMultiLabelSegmentationUtilitiesView, context)
m_Context = context;
}
void mitk::PluginActivator::stop(ctkPluginContext*)
{
}
ctkPluginContext* mitk::PluginActivator::getContext()
{
return m_Context;
}
diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox
index 03b1b480b9..bea16813ce 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox
+++ b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox
@@ -1,325 +1,325 @@
/**
\page org_mitk_views_segmentation The Segmentation View
\imageMacro{segmentation-dox.svg,"Icon of the segmentation view",2.00}
<i>Some of the features described below are closed source additions to the open source toolkit MITK and are not available in every application.</i>
\tableofcontents
\section org_mitk_views_segmentationUserManualOverview Overview
Segmentation is the act of partitioning an image into subsets by either manual or automated delineation to create i.e. a distinction between foreground and background.
The MITK <b>segmentation plugin</b> allows you to create segmentations of anatomical and pathological structures in medical images.
The plugin consists of a number of views:
<ul>
<li> <b>Segmentation View</b>: Manual and (semi-)automatic segmentation
<li> \subpage org_mitk_views_segmentationutilities : Segmentation post-processing
</ul>
In this documentation, the features and usage of the segmentation view will be described.
For an introduction to the segmentation utilities and clipping plane views, please be referred to the respective documentation pages.
\imageMacro{QmitkSegmentationPlugin_Overview.png,"Segmentation plugin overview", 16.00}
\section org_mitk_views_segmentationPreferences Preferences
The segmentation plugin offers a number of preferences which can be set via the MITK Workbench application preferences:
\imageMacro{QmitkSegmentationPreferences.png,"Segmentation preferences", 10.00}
<ul>
<li> <b>Slim view:</b> Allows you to show or hide the tool button description of the segmentation view
<li> <b>2D display:</b> Specify whether the segmentation is drawn as outline or as a transparent overlay
<li> <b>3D display:</b> Activate 3D volume rendering for your segmentation
<li> <b>Data node selection mode:</b> If activated the segmentation image is automatically chosen from the data manager selection.
<li> <b>Smoothed surface creation:</b> Set certain smoothing parameters for surface creation
</ul>
\section org_mitk_views_segmentationUserManualTechnical Technical Issues
The segmentation plugin makes a number of assumptions:
<ul>
<li> Images must be 2D, 3D, or 3D+t.
<li> Images must be single-values, i.e. CT, MRI or "normal" ultrasound. Images from color doppler or photographic (RGB) images are only partially supported (please be aware that some tools might not be compatible with this image type).
<li> Segmentations are handled as binary images of the same extent as the original image.
</ul>
\section org_mitk_views_segmentationUserManualImageSelection Data Selection & Creating New Segmentations
To select a reference image for the segmentation, click on the <i>Patient Image</i> selection widget and choose a suitable image from the selection available in the data manager.
By default the auto selection mode is enabled (see \ref org_mitk_views_segmentationPreferences).\n
Once a patient image is selected, a new segmentation can be created on this reference image by clicking the <i>New...</i> button to the right of the <i>Segmentation</i> selection widget.
An input field will appear which allows you to set the name and display color of the segmentation. Notice that the input field suggests names once you start typing and that it also suggests colors for known organ names.
Once generated the segmentation will be added with "binary mask" icon to the data manager as sub-node of the reference image. This item is automatically selected for you, allowing you to start editing the new segmentation right away.
\subsection org_mitk_views_segmentationUserManualManualKringeling2 Selecting Segmentations for Editing
Alternatively to creating a new segmentation, an existing one can be edited as well.
As you might have segmented multiple structures within a single image, the application needs to know which of them to use for editing.
For that you click the segmentation selection widget and a selection field will open, containing all suitable segmentations for the parent dataset available in the data manager.
\section org_mitk_views_segmentationUserManualToolOverview Segmentation Tool Overview
MITK offers a comprehensive set of slice-based 2D and (semi-)automated 3D segmentation tools.
The manual 2D tools require some user interaction and can only be applied to a single image slice whereas the 3D tools operate on the whole image. The 3D tools usually only
require a small amount of user interaction, i.e. placing seed points or setting/adjusting parameters.
You can switch between the different toolsets by selecting the 2D or 3D tab in the segmentation view.
\imageMacro{QmitkSegmentation_ToolOverview.png,"An overview of the existing 2D and 3D tools in MITK.",5.50}
\section org_mitk_views_segmentationUserManualManualKringeling 2D Segmentation Tools
With 2D manual contouring you define which voxels are part of the segmentation and which ones are not. This allows you to create segmentations of any structures of interest in an image.
You can also use manual contouring to correct segmentations that result from sub-optimal automatic methods.
The drawback of manual contouring is that you might need to define contours on many 2D slices. However, this is mitigated by the interpolation feature, which will make suggestions for a segmentation.
\subsection org_mitk_views_segmentationUserManualManualKringeling3 Selecting Editing Tools
To start using one of the editing tools, click its button from the displayed toolbox.
The selected editing tool will be active and its corresponding button will stay pressed until you click the button again.
Selecting a different tool also deactivates the previous one.\n
If you have to delineate a lot of images, shortcuts to switch between tools becomes convenient. For that, just hit the first letter of each tool to activate it (A for Add, S for Subtract, etc.).
\subsection org_mitk_views_segmentationUserManualManualKringeling4 Using Editing Tools
All of the editing tools work by the same principle: you use the mouse (left button) to click anywhere in a 2D window (any of the orientations axial, sagittal, or coronal),
move the mouse while holding the mouse button and release to finish the editing action.
Multi-step undo and redo is fully supported by all editing tools. Use the application-wide undo button in the toolbar to revert erroneous %actions.
<i>Remark</i>: If you are familiar with the MITK Workbench, you know that clicking and moving the mouse in any of the 2D render windows will move around the crosshair that defines what part of the image is displayed.
This behavior is disabled whilst any of the manual segmentation tools are active- otherwise you might have a hard time concentrating on the contour you are drawing.
\subsection org_mitk_views_segmentationUserManualAddSubtractTools Add and Subtract Tools
\imageMacro{QmitkSegmentation_IMGIconAddSubtract.png,"Add and Subtract Tools",7.70}
Use the left mouse button to draw a closed contour. When releasing the mouse button, the contour will be added (Add tool) to or removed (Subtract tool) from the current segmentation.
Adding and subtracting voxels can be iteratively repeated for the same segmentation. Holding CTRL / CMD while drawing will invert the current tool's behavior (i.e. instead of adding voxels, they will be subtracted).
\subsection org_mitk_views_segmentationUserManualPaintWipeTools Paint and Wipe Tools
\imageMacro{QmitkSegmentation_IMGIconPaintWipe.png,"Paint and Wipe Tools",7.68}
Use the <i>Size</i> slider to change the radius of the round paintbrush tool. Move the mouse in any 2D window and press the left button to draw or erase pixels.
Holding CTRL / CMD while drawing will invert the current tool's behavior (i.e. instead of painting voxels, they will be wiped).
\subsection org_mitk_views_segmentationUserManualRegionGrowingTool Region Growing Tool
\imageMacro{QmitkSegmentation_IMGIconRegionGrowing.png,"Region Growing Tool",3.81}
Click at one point in a 2D slice widget to add an image region to the segmentation with the region growing tool.
Region Growing selects all pixels around the mouse cursor that have a similar gray value as the pixel below the mouse cursor. This enables you to quickly create segmentations of structures that have a good contrast to surrounding tissue.
The tool operates based on the current level window, so changing the level window to optimize the contrast for the ROI is encouraged.
Moving the mouse up/down is different from left/right: Moving up the cursor while holding the left mouse button widens the range for the included grey values; moving it down narrows it. Moving the mouse left and right will shift the range.
The tool will select more or less pixels, corresponding to the changing gray value range.
\if THISISNOTIMPLEMENTEDATTHEMOMENT
A common issue with region growing is the so called "leakage" which happens when the structure of interest is connected to other pixels, of similar gray values, through a narrow "bridge" at the border of the structure.
The Region Growing tool comes with a "leakage detection/removal" feature. If leakage happens, you can left-click into the leakage region and the tool will try to automatically remove this region (see illustration below).
\imageMacro{QmitkSegmentation_IMGLeakage.png,"Leakage correction feature of the Region Growing tool",11.28}
\endif
\subsection org_mitk_views_segmentationUserManualFillTool Fill Tool
\imageMacro{QmitkSegmentation_IMGIconFill.png,"Fill Tool",3.81}
Left-click inside a segmentation with holes to completely fill all holes. Left-click inside a hole to fill only this specific hole.
\subsection org_mitk_views_segmentationUserManualEraseTool Erase Tool
\imageMacro{QmitkSegmentation_IMGIconErase.png,"Erase Tool",3.79}
This tool removes a connected part of pixels that form a segmentation. You may use it to remove single segmentations (left-click on specific segmentation) or to clear a whole slice at once (left-click outside a segmentation).
\subsection org_mitk_views_segmentationUserManualLiveWireTool Live Wire Tool
\imageMacro{QmitkSegmentation_IMGIconLiveWire.png,"Live Wire Tool",3.01}
The Live Wire Tool acts as a magnetic lasso with a contour snapping to edges of objects.
\imageMacro{QmitkSegmentation_IMGLiveWireUsage.PNG,"Steps for using the Live Wire Tool",16.00}
<ul>
<li>(1) To start the tool you have to double-click near the edge of the object you want to segment. The initial anchor point will snap to the edge within a 3x3 region.
<li>(2) Move the mouse. You don't have trace the edge of the object. The contour will automatically snap to it.
<li>(3) To fix a segment you can set anchor points by single left mouse button click.
<li>(4) Go on with moving the mouse and setting anchor points.
<li>(5) To close the contour double-click on the initial anchor point.
<li>(6) After closing, the contour can be edited by moving, inserting and deleting anchor points.
</ul>
The contour will be transferred to its binary image representation by deactivating the tool.
\subsection org_mitk_views_segmentationUserManual2DFastMarchingTool 2D Fast Marching Tool
\imageMacro{QmitkSegmentation_IMG2DFastMarchingUsage.png,"2D Fast Marching Tool",3.01}
Provides a fast marching based 2D interaction segmentation tool. You start with setting seed points in an image slice. Via several sliders you can adapt parameters and see the fast marching result instantly.
\subsection org_mitk_views_segmentationUserManualManualKringeling5 2D and 3D Interpolation
Creating segmentations using 2D manual contouring for large image volumes may be very time-consuming, because structures of interest may cover a large range of slices.
The segmentation view offers two helpful features to mitigate this drawback:
<ul>
<li> 2D Interpolation
<li> 3D Interpolation
</ul>
The <b>2D Interpolation</b> creates suggestions for a segmentation whenever you have a slice that
<ul>
<li> has got neighboring slices with segmentations (these do not need to be direct neighbors but could also be a couple of slices away) AND
<li> is completely clear of a manual segmentation, i.e. there will be no suggestion if there is even only a single pixel of segmentation in the current slice.
</ul>
\imageMacro{QmitkSegmentation_2DInterpolation.png,"2D Interpolation Usage",3.01}
Interpolated suggestions are displayed as outlines, until you confirm them as part of the segmentation.
To confirm single slices, click the <i>Confirm for single slice</i> button below the toolbox. You may also review the interpolations visually and then accept all of them at once by selecting <i>Confirm for all slices</i>.
The <b>3D interpolation</b> creates suggestions for 3D segmentations. That means if you start contouring, from the second contour onwards, the surface of the segmented area will be interpolated based on the given contour information.
The interpolation works with all available manual tools. Please note that this is currently a pure mathematical interpolation, i.e. image intensity information is not taken into account.
With each further contour the interpolation result will be improved, but the more contours you provide the longer the recalculation will take.
To achieve an optimal interpolation result and in this way a most accurate segmentation you should try to describe the surface with sparse contours by segmenting in arbitrary
oriented planes. The 3D interpolation is not meant to be used for parallel slice-wise segmentation, but rather segmentations in i.e. the axial, coronal and sagittal plane.
\imageMacro{QmitkSegmentation_3DInterpolationWrongRight.png,"3D Interpolation Usage",16.00}
You can accept the interpolation result by clicking the <i>Confirm</i>-button below the tool buttons.
In this case the 3D interpolation will be deactivated automatically so that the result can be post-processed without any interpolation running in the background.
Additional to the surface, black contours are shown in the 3D render window, which mark all the drawn contours used for the interpolation.
You can navigate between the drawn contours by clicking on the corresponding <i>position</i> nodes in the data manager which are stored as sub-nodes of the selected segmentation.
If you do not want to see these nodes just uncheck the <i>Show Position Nodes</i> checkbox and these nodes will be hidden.
If you want to delete a drawn contour we recommend to use the Erase-Tool since Redo/Undo is not yet working for 3D interpolation.
The current state of the 3D interpolation can be saved across application restart. For that, just click on save project during the interpolation is active.
After restarting the application and load your project you can click on "Reinit Interpolation" within the 3D interpolation GUI area.
\section org_mitk_views_segmentationUserManual3DSegmentationTools 3D Segmentation Tools
The 3D tools operate on the whole image and require usually a small amount of interaction like placing seed-points or specifying certain parameters. All 3D tools provide
an immediate segmentation feedback, which is displayed as a transparent green overlay. For accepting a preview you have to press the <i>Confirm</i> button of the selected tool.
The following 3D tools are available:
\subsection org_mitk_views_segmentationUserManual3DThresholdTool 3D Threshold Tool
The thresholding tool simply applies a 3D threshold to the patient image. All pixels with values equal or above the selected threshold are labeled as part of the segmentation.
You can change the threshold by either moving the slider of setting a certain value in the spinbox.
\imageMacro{QmitkSegmentation_3DThresholdTool.png,"3D Threshold tool",10.00}
\subsection org_mitk_views_segmentationUserManual3DULTool 3D Upper/Lower Threshold Tool
The Upper/Lower Thresholding tool works similar to the simple 3D threshold tool but allows you to define an upper and lower threshold. All pixels with
values within this threshold interval will be labeled as part of the segmentation.
\imageMacro{QmitkSegmentation_3DULThresholdTool.png,"3D Upper/Lower Threshold tool",10.00}
\subsection org_mitk_views_segmentationUserManual3DOtsuTool 3D Otsu Tool
The 3D Otsu tool provides a more sophisticated thresholding algorithm. It allows you to define a number of regions. Based on the image histogram the pixels will
then be divided into different regions. The more regions you define the longer the calculation will take. You can select afterwards which of these regions you want to confirm as segmentation.
\imageMacro{QmitkSegmentation_3DOtsuTool.png,"3D Otsu tool",10.00}
\subsection org_mitk_views_segmentationUserManual3DFMTool 3D Fast Marching Tool
The 3D Fast Marching tool works similar to the 2D pendant but on the whole image. Depending on your image size the calculation might take some time.
You can interactively set the parameters of the algorithm via the GUI. The resulting segmentation will be automatically updated.
\imageMacro{QmitkSegmentation_3DFMTool.png,"3D Fast Marching tool",10.00}
\subsection org_mitk_views_segmentationUserManual3DRGTool 3D Region Growing Tool
The 3D Region Growing tool works similar to the 2D pendant. At the beginning you have to place a seedpoint and define a threshold interval. If you press
<i>Run Segmentation</i> a preview is calculated. By moving the <i>Adapt region growing</i> slider you can interactively adapt the segmentation result.
\imageMacro{QmitkSegmentation_3DRGTool.png,"3D Region Growing tool",10.00}
\subsection org_mitk_views_segmentationUserManual3DWatershedTool 3D Watershed Tool
This tool provides a watershed based segmentation algorithm. For a detailed explanation of the parameters level and threshold, please be referred to https://itk.org/Doxygen/html/classitk_1_1WatershedImageFilter.html .
Remark: The tool is (due to its implementation) faster if you use lower levels. So in doubt you should start with high levels (supposed to undersegment) and then lower the levels until you are happy.
\imageMacro{QmitkSegmentation_3DWatershedTool.png,"3D Watershed tool",10.00}
\subsection org_mitk_views_segmentationUserManualPickingTool Picking Tool
The Picking tool allows you to select islands within your segmentation. This is especially useful if e.g. a thresholding provided you with several areas within
your image but you are just interested in one special region.
\imageMacro{QmitkSegmentation_PickingTool.png,"Picking tool",10.00}
\subsection org_mitk_views_segmentationUserManualnnUNetTool nnU-Net Tool (Ubuntu only)
This tool provides a GUI to the deep learning-based segmentation algorithm called the nnUNet. With this tool, you can get a segmentation mask predicted for the loaded image in MITK. Be ready with the pre-trained weights (a.k.a <b>RESULTS_FOLDER</b>)
for your organ or task concerned, before using the tool. For a detailed explanation of the parameters and pre-trained weights folder structure etc., please refer to https://github.com/MIC-DKFZ/nnUNet. <br>
Remark: The tool assumes that you have a Python3 environment with nnUNet (pip) installed. Your machine should be also equipped with a CUDA enabled GPU.
-\imageMacro{QmitkSegmentation_3DWatershedTool.png,"nnUNet tool",10.00}
+\imageMacro{QmitkSegmentation_nnUnetTool.png,"nnUNet tool",10.00}
\subsubsection org_mitk_views_segmentationUserManualnnUNetToolWorkflow Workflow:
-# Click on the "nnUNet Results Folder" directory icon and navigate to the results folder on your hard disk. This is equivalent to setting the <b>RESULTS_FOLDER</b> environment variable. If your results folder is as
per the nnUNet required folder structure, the configuration, trainers, tasks and folds are automatically parsed and correspondingly loaded in the drop-down boxes as shown below.
\imageMacro{QmitkSegmentation_nnUNet_Settings.png,"nnUNet Segmentation Settings",10}
-# Choose your required configuration-task-trainer-fold parameters, sequentially. By default, all entries are selected inside the "Fold" dropdown (shown: "All").
Note that, if you uncheck all entries from the "Fold" dropdown (shown: "None"), then too, all folds would be considered for inferencing.
-# For ensemble predictions, you will get the option to select parameters irrespective on postprocessing files available in the ensembles folder of <b>RESULTS_FOLDER</b>.
Note that, if a postprocessing json file exists for the selected combination then it will used for ensembling, by default. To choose not to, uncheck the "Use PostProcessing JSON" in the "Advanced" section.
\imageMacro{QmitkSegmentation_nnUNet_ensemble.png,"nnUNet Segmentation Settings",10}
-# If your task requires multi-modal inputs, then check the "Multi-Modal" checkbox and enter the no.of modalities additionally required for inferencing, in the "No. of Extra Modalities" spinbox.
Instantly, as much node selectors should appear below to select other image nodes from the Data Manager along including a selector with preselected with the reference node.
Certain pre-trained configurations are trained in a particular modal order. Please be aware of the order beforehand & select the image nodes in the node selectors accordingly for accurate inferencing.
\imageMacro{QmitkSegmentation_nnUNet_multimodal.png,"nnUNet Multi Modal Settings",10.00}
-# Select the "Python Path" drop-down to see if MITK has automatically detected other Python environments.
Click on a fitting environment for the nnUNet inference or click "Select" in the dropdown to choose an unlisted python environment. Note that, while selecting an arbitrary environment folder, only select the base folder.
No need to select all the way until "../myenv/bin/python", for example.
-# Click on "Preview".
\imageMacro{QmitkSegmentation_nnUNet_Advanced.png,"nnUNet Advanced Settings",10.00}
-# In the "Advanced" section, you can also activate other options like "Mixed Precision" and "Enable Mirroring" (for test time data augmentation) pertaining to nnUNet.
-# Use "GPU Id" to change the preferred GPU for inferencing. This is equivalent to setting the <b>CUDA_VISIBLE_DEVICES</b> environment variable.
-# Every inferred segmentation is cached to prevent a redundant computation. In case, a user doesn't wish to cache a Preview, uncheck the "Enable Caching" in the "Advanced" section. This will ensure that the
current parameters will neither be checked against the existing cache nor a segmentation be loaded from it.
-# You may always clear all the cached segmentations by clicking "Clear Cache" button.
\subsubsection org_mitk_views_segmentationUserManualnnUNetToolMisc Miscellaneous:
-# In case you want to reload/reparse the folders in the "nnUNet Results Folder", eg. after adding new tasks into it, you may do so without reselecting the folder again by clicking the "Refresh Results Folder" button.
-# If you have a nnUNet code-base yet to be pip-installed into any environment (eg. developer modified nnUNet forks), click checkbox "No pip".
Once the checkbox is checked a new directory selector appears in the section. You may click and navigate to your nnUNet fork folder to select the code from which you want inference.
\imageMacro{QmitkSegmentation_nnUNet_nopip.png,"nnUNet Advanced Settings with option to select nnUNet local folder",10.00}
-# The "Advanced" > "GPU Id" combobox lists the Nvidia GPUs available by parsing the <tt>nvidia-smi</tt> utility output. In case your machine has Nvidia CUDA enabled GPUs but the <tt>nvidia-smi</tt> fails for some reason, the "GPU Id" combobox will show no entries.
In such a situation, it's still possible to execute inferencing by manually entering the preferred GPU Id, eg. 0 in the combobox.
\section org_mitk_views_segmentationUserManualPostprocessing Additional things you can do with segmentations
Segmentations are never an end in themselves. Consequently, the segmentation view adds a couple of "post-processing" actions, accessible through the context-menu of the data manager.
\imageMacro{QmitkSegmentation_IMGDataManagerContextMenu.png,"Context menu items for segmentations.",10.58}
<ul>
<li> <b>Create polygon %model</b> applies the marching cubes algorithm to the segmentation. This polygon %model can be used for visualization in 3D or other applications such as stereolithography (3D printing).
<li> <b>Create smoothed polygon %model</b> uses smoothing in addition to the marching cubes algorithm, which creates models that do not follow the exact outlines of the segmentation, but look smoother.
<li> <b>Autocrop</b> can save memory. Manual segmentations have the same extent as the patient image, even if the segmentation comprises only a small sub-volume. This invisible and meaningless margin is removed by autocropping.
</ul>
\section org_mitk_views_segmentationof3DTImages Segmentation of 3D+t images
For segmentation of 3D+t images, some tools give you the option to choose between creating dynamic and static masks.
<ul>
<li> Dynamic masks can be created on each time frame individually.
<li> Static masks will be defined on one time frame and will be the same for all other time frames.
</ul>
In general, segmentation is applied on the time frame that is selected when execution is performed. If you alter the time frame, the segmentation preview is adapted.
\section org_mitk_views_segmentationUserManualTechnicalDetail Technical Information for Developers
For technical specifications see \subpage QmitkSegmentationTechnicalPage and for information on the extensions of the tools system \subpage toolextensions .
*/
diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUnetTool.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUnetTool.png
new file mode 100644
index 0000000000..8bab0fda52
Binary files /dev/null and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUnetTool.png differ
diff --git a/Plugins/org.mitk.gui.qt.segmentation/files.cmake b/Plugins/org.mitk.gui.qt.segmentation/files.cmake
index a9b25997b4..f63907b3ae 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/files.cmake
+++ b/Plugins/org.mitk.gui.qt.segmentation/files.cmake
@@ -1,70 +1,80 @@
set(SRC_CPP_FILES
QmitkSegmentationPreferencePage.cpp
)
set(INTERNAL_CPP_FILES
mitkPluginActivator.cpp
QmitkSegmentationView.cpp
- QmitkCreatePolygonModelAction.cpp
QmitkAutocropAction.cpp
QmitkAutocropLabelSetImageAction.cpp
+ QmitkCreatePolygonModelAction.cpp
+ QmitkLoadMultiLabelPresetAction.cpp
+ QmitkSaveMultiLabelPresetAction.cpp
Common/QmitkDataSelectionWidget.cpp
+ Common/QmitkLabelsWidget.cpp
+ Common/QmitkLayersWidget.cpp
SegmentationUtilities/QmitkSegmentationUtilitiesView.cpp
SegmentationUtilities/QmitkSegmentationUtilityWidget.cpp
SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidget.cpp
SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp
SegmentationUtilities/ContourModelToImage/QmitkContourModelToImageWidget.cpp
SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidget.cpp
SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.cpp
)
set(UI_FILES
- src/internal/QmitkSegmentationControls.ui
+ src/internal/QmitkSegmentationViewControls.ui
src/internal/Common/QmitkDataSelectionWidgetControls.ui
+ src/internal/Common/QmitkLabelsWidgetControls.ui
+ src/internal/Common/QmitkLayersWidgetControls.ui
src/internal/SegmentationUtilities/QmitkSegmentationUtilitiesViewControls.ui
src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidgetControls.ui
src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui
src/internal/SegmentationUtilities/ContourModelToImage/QmitkContourModelToImageWidgetControls.ui
src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidgetControls.ui
src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidgetControls.ui
)
set(MOC_H_FILES
src/QmitkSegmentationPreferencePage.h
src/internal/mitkPluginActivator.h
src/internal/QmitkSegmentationView.h
- src/internal/QmitkCreatePolygonModelAction.h
src/internal/QmitkAutocropAction.h
src/internal/QmitkAutocropLabelSetImageAction.h
+ src/internal/QmitkCreatePolygonModelAction.h
+ src/internal/QmitkLoadMultiLabelPresetAction.h
+ src/internal/QmitkSaveMultiLabelPresetAction.h
src/internal/Common/QmitkDataSelectionWidget.h
+ src/internal/Common/QmitkLabelsWidget.h
+ src/internal/Common/QmitkLayersWidget.h
src/internal/SegmentationUtilities/QmitkSegmentationUtilitiesView.h
src/internal/SegmentationUtilities/QmitkSegmentationUtilityWidget.h
src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidget.h
src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.h
src/internal/SegmentationUtilities/ContourModelToImage/QmitkContourModelToImageWidget.h
src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidget.h
src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.h
)
set(CACHED_RESOURCE_FILES
resources/segmentation.svg
resources/segmentation_utilities.svg
plugin.xml
)
set(QRC_FILES
resources/segmentation.qrc
resources/SegmentationUtilities.qrc
resources/BooleanOperationsWidget.qrc
resources/MorphologicalOperationsWidget.qrc
)
set(CPP_FILES)
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/Plugins/org.mitk.gui.qt.segmentation/plugin.xml b/Plugins/org.mitk.gui.qt.segmentation/plugin.xml
index a628b56818..b0753685a8 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/plugin.xml
+++ b/Plugins/org.mitk.gui.qt.segmentation/plugin.xml
@@ -1,81 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<plugin>
<extension point="org.blueberry.ui.views">
<view id="org.mitk.views.segmentation"
name="Segmentation"
category="Segmentation"
icon="resources/segmentation.svg"
class="QmitkSegmentationView" >
<description>Allows the segmentation of images using different tools.</description>
<keywordReference id="org.mitk.views.segmentation.ViewKeyword"/>
</view>
<view
id="org.mitk.views.segmentationutilities"
name="Segmentation Utilities"
category="Segmentation"
class="QmitkSegmentationUtilitiesView"
icon="resources/segmentation_utilities.svg" >
<description>Edit segmentations using standard operations. </description>
<keywordReference id="org.mitk.views.segmentationutilities.ViewKeyword"/>
</view>
</extension>
<extension point="org.blueberry.ui.preferencePages">
<page id="org.mitk.gui.qt.application.SegmentationPreferencePage" name="Segmentation" class="QmitkSegmentationPreferencePage">
<keywordreference id="org.mitk.gui.qt.application.SegmentationPreferencePageKeywords"></keywordreference>
</page>
</extension>
<extension point="org.blueberry.ui.keywords">
<keyword id="org.mitk.gui.qt.application.SegmentationPreferencePageKeywords" label="data manager"></keyword>
</extension>
<extension point="org.mitk.gui.qt.datamanager.contextMenuActions">
<contextMenuAction nodeDescriptorName="ImageMask" label="Create polygon model" icon="" smoothed="false" class="QmitkCreatePolygonModelAction" />
<contextMenuAction nodeDescriptorName="ImageMask" label="Create smoothed polygon model" icon="" smoothed = "true" class="QmitkCreatePolygonModelAction" />
<contextMenuAction nodeDescriptorName="ImageMask" label="Autocrop" icon="" class="QmitkAutocropAction" />
<contextMenuAction nodeDescriptorName="LabelSetImage" label="Create polygon model" icon="" smoothed="false" class="QmitkCreatePolygonModelAction" />
<contextMenuAction nodeDescriptorName="LabelSetImage" label="Create smoothed polygon model" icon="" smoothed = "true" class="QmitkCreatePolygonModelAction" />
<contextMenuAction nodeDescriptorName="LabelSetImage" label="Autocrop" icon="" class="QmitkAutocropLabelSetImageAction" />
+ <contextMenuAction nodeDescriptorName="LabelSetImage" label="Save LabelSet Preset" icon="" class="QmitkSaveMultiLabelPresetAction" />
+ <contextMenuAction nodeDescriptorName="LabelSetImage" label="Load LabelSet Preset" icon="" class="QmitkLoadMultiLabelPresetAction" />
</extension>
<extension point="org.blueberry.ui.keywords">
<keyword id="org.mitk.views.segmentation.ViewKeyword" label="Segmentation" />
<keyword id="org.mitk.views.segmentation.ViewKeyword" label="Otsu" />
- <keyword id="org.mitk.views.segmentation.ViewKeyword" label="Correction" />
<keyword id="org.mitk.views.segmentation.ViewKeyword" label="Paint" />
<keyword id="org.mitk.views.segmentation.ViewKeyword" label="Wipe" />
<keyword id="org.mitk.views.segmentation.ViewKeyword" label="Region Growing" />
<keyword id="org.mitk.views.segmentation.ViewKeyword" label="Fill" />
<keyword id="org.mitk.views.segmentation.ViewKeyword" label="Erase" />
<keyword id="org.mitk.views.segmentation.ViewKeyword" label="Live Wire" />
<keyword id="org.mitk.views.segmentation.ViewKeyword" label="Fast Marching" />
<keyword id="org.mitk.views.segmentation.ViewKeyword" label="Threshold" />
<keyword id="org.mitk.views.segmentation.ViewKeyword" label="Watershed" />
<keyword id="org.mitk.views.segmentation.ViewKeyword" label="Picking" />
<keyword id="org.mitk.views.segmentation.ViewKeyword" label="Watershed" />
<keyword id="org.mitk.views.segmentation.ViewKeyword" label="Annotation annote" />
<keyword id="org.mitk.views.segmentation.ViewKeyword" label="Labeling" />
<keyword id="org.mitk.views.segmentation.ViewKeyword" label="Tools" />
<keyword id="org.mitk.views.segmentation.ViewKeyword" label="nnUNet" />
<keyword id="org.mitk.views.segmentationutilities.ViewKeyword" label="Segmentation" />
<keyword id="org.mitk.views.segmentationutilities.ViewKeyword" label="Difference" />
<keyword id="org.mitk.views.segmentationutilities.ViewKeyword" label="Intersection" />
<keyword id="org.mitk.views.segmentationutilities.ViewKeyword" label="Union" />
<keyword id="org.mitk.views.segmentationutilities.ViewKeyword" label="Image Masking" />
<keyword id="org.mitk.views.segmentationutilities.ViewKeyword" label="Surface Masking" />
<keyword id="org.mitk.views.segmentationutilities.ViewKeyword" label="Dilatation" />
<keyword id="org.mitk.views.segmentationutilities.ViewKeyword" label="Erosion" />
<keyword id="org.mitk.views.segmentationutilities.ViewKeyword" label="Closing" />
<keyword id="org.mitk.views.segmentationutilities.ViewKeyword" label="Opening" />
<keyword id="org.mitk.views.segmentationutilities.ViewKeyword" label="Fill Holes" />
<keyword id="org.mitk.views.segmentationutilities.ViewKeyword" label="Morphological Operations" />
<keyword id="org.mitk.views.segmentationutilities.ViewKeyword" label="Boolean Operations" />
<keyword id="org.mitk.views.segmentationutilities.ViewKeyword" label="Surface to Image" />
<keyword id="org.mitk.views.segmentationutilities.ViewKeyword" label="Surface creation" />
<keyword id="org.mitk.views.segmentationutilities.ViewKeyword" label="Surface operations" />
</extension>
</plugin>
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp
index a79f58c5bf..e49cce88d5 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp
@@ -1,175 +1,176 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "QmitkSegmentationPreferencePage.h"
#include <QLabel>
#include <QPushButton>
#include <QFormLayout>
#include <QCheckBox>
#include <QGroupBox>
#include <QRadioButton>
#include <QMessageBox>
#include <QDoubleSpinBox>
#include <berryIPreferencesService.h>
#include <berryPlatform.h>
QmitkSegmentationPreferencePage::QmitkSegmentationPreferencePage()
: m_MainControl(nullptr),
m_SlimViewCheckBox(nullptr),
m_RadioOutline(nullptr),
m_RadioOverlay(nullptr),
m_SelectionModeCheckBox(nullptr),
m_SmoothingCheckBox(nullptr),
m_SmoothingSpinBox(nullptr),
m_DecimationSpinBox(nullptr),
m_ClosingSpinBox(nullptr),
m_Initializing(false)
{
}
QmitkSegmentationPreferencePage::~QmitkSegmentationPreferencePage()
{
}
-void QmitkSegmentationPreferencePage::Init(berry::IWorkbench::Pointer )
+void QmitkSegmentationPreferencePage::Init(berry::IWorkbench::Pointer)
{
}
void QmitkSegmentationPreferencePage::CreateQtControl(QWidget* parent)
{
m_Initializing = true;
berry::IPreferencesService* prefService = berry::Platform::GetPreferencesService();
m_SegmentationPreferencesNode = prefService->GetSystemPreferences()->Node("/org.mitk.views.segmentation");
m_MainControl = new QWidget(parent);
- auto formLayout = new QFormLayout;
+ auto formLayout = new QFormLayout;
formLayout->setHorizontalSpacing(8);
formLayout->setVerticalSpacing(24);
m_SlimViewCheckBox = new QCheckBox("Hide tool button texts and increase icon size", m_MainControl);
formLayout->addRow("Slim view", m_SlimViewCheckBox);
- auto displayOptionsLayout = new QVBoxLayout;
- m_RadioOutline = new QRadioButton( "Draw as outline", m_MainControl);
- displayOptionsLayout->addWidget( m_RadioOutline );
- m_RadioOverlay = new QRadioButton( "Draw as transparent overlay", m_MainControl);
- displayOptionsLayout->addWidget( m_RadioOverlay );
- formLayout->addRow( "2D display", displayOptionsLayout );
+ auto displayOptionsLayout = new QVBoxLayout;
+ m_RadioOutline = new QRadioButton("Draw as outline", m_MainControl);
+ displayOptionsLayout->addWidget(m_RadioOutline);
+ m_RadioOverlay = new QRadioButton("Draw as transparent overlay", m_MainControl);
+ displayOptionsLayout->addWidget(m_RadioOverlay);
+ formLayout->addRow("2D display", displayOptionsLayout);
- m_SelectionModeCheckBox = new QCheckBox("Enable auto-selection mode", m_MainControl);
- m_SelectionModeCheckBox->setToolTip("Automatically select a patient image and a segmentation if available");
+ m_SelectionModeCheckBox = new QCheckBox("Show only selected nodes", m_MainControl);
+ m_SelectionModeCheckBox->setToolTip("If checked the segmentation plugin ensures that only the selected segmentation"
+ "and the reference image are visible at one time.");
formLayout->addRow("Data node selection mode", m_SelectionModeCheckBox);
- auto surfaceLayout = new QFormLayout;
+ auto surfaceLayout = new QFormLayout;
surfaceLayout->setSpacing(8);
m_SmoothingCheckBox = new QCheckBox("Use image spacing as smoothing value hint", m_MainControl);
surfaceLayout->addRow(m_SmoothingCheckBox);
connect(m_SmoothingCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSmoothingCheckboxChecked(int)));
m_SmoothingSpinBox = new QDoubleSpinBox(m_MainControl);
m_SmoothingSpinBox->setMinimum(0.0);
m_SmoothingSpinBox->setSingleStep(0.5);
m_SmoothingSpinBox->setValue(1.0);
m_SmoothingSpinBox->setToolTip("The Smoothing value is used as variance for a gaussian blur.");
surfaceLayout->addRow("Smoothing value (mm)", m_SmoothingSpinBox);
m_DecimationSpinBox = new QDoubleSpinBox(m_MainControl);
m_DecimationSpinBox->setMinimum(0.0);
m_DecimationSpinBox->setMaximum(0.99);
m_DecimationSpinBox->setSingleStep(0.1);
m_DecimationSpinBox->setValue(0.5);
- m_DecimationSpinBox->setToolTip("Valid range is [0, 1). High values increase decimation, especially when very close to 1. A value of 0 disables decimation.");
+ m_DecimationSpinBox->setToolTip("Valid range is [0, 1). High values increase decimation, especially when very close "
+ "to 1. A value of 0 disables decimation.");
surfaceLayout->addRow("Decimation rate", m_DecimationSpinBox);
m_ClosingSpinBox = new QDoubleSpinBox(m_MainControl);
m_ClosingSpinBox->setMinimum(0.0);
m_ClosingSpinBox->setMaximum(1.0);
m_ClosingSpinBox->setSingleStep(0.1);
m_ClosingSpinBox->setValue(0.0);
m_ClosingSpinBox->setToolTip("Valid range is [0, 1]. Higher values increase closing. A value of 0 disables closing.");
surfaceLayout->addRow("Closing Ratio", m_ClosingSpinBox);
formLayout->addRow("Smoothed surface creation", surfaceLayout);
m_MainControl->setLayout(formLayout);
this->Update();
m_Initializing = false;
}
QWidget* QmitkSegmentationPreferencePage::GetQtControl() const
{
return m_MainControl;
}
bool QmitkSegmentationPreferencePage::PerformOk()
{
m_SegmentationPreferencesNode->PutBool("slim view", m_SlimViewCheckBox->isChecked());
m_SegmentationPreferencesNode->PutBool("draw outline", m_RadioOutline->isChecked());
+ m_SegmentationPreferencesNode->PutBool("selection mode", m_SelectionModeCheckBox->isChecked());
m_SegmentationPreferencesNode->PutBool("smoothing hint", m_SmoothingCheckBox->isChecked());
m_SegmentationPreferencesNode->PutDouble("smoothing value", m_SmoothingSpinBox->value());
m_SegmentationPreferencesNode->PutDouble("decimation rate", m_DecimationSpinBox->value());
m_SegmentationPreferencesNode->PutDouble("closing ratio", m_ClosingSpinBox->value());
- m_SegmentationPreferencesNode->PutBool("auto selection", m_SelectionModeCheckBox->isChecked());
return true;
}
void QmitkSegmentationPreferencePage::PerformCancel()
{
-
}
void QmitkSegmentationPreferencePage::Update()
{
m_SlimViewCheckBox->setChecked(m_SegmentationPreferencesNode->GetBool("slim view", false));
- if (m_SegmentationPreferencesNode->GetBool("draw outline", true) )
+ if (m_SegmentationPreferencesNode->GetBool("draw outline", true))
{
m_RadioOutline->setChecked(true);
}
else
{
m_RadioOverlay->setChecked(true);
}
- m_SelectionModeCheckBox->setChecked(m_SegmentationPreferencesNode->GetBool("auto selection", true));
+ m_SelectionModeCheckBox->setChecked(m_SegmentationPreferencesNode->GetBool("selection mode", false));
if (m_SegmentationPreferencesNode->GetBool("smoothing hint", true))
{
m_SmoothingCheckBox->setChecked(true);
m_SmoothingSpinBox->setDisabled(true);
}
else
{
- m_SmoothingCheckBox->setChecked(false);
- m_SmoothingSpinBox->setEnabled(true);
+ m_SmoothingCheckBox->setChecked(false);
+ m_SmoothingSpinBox->setEnabled(true);
}
m_SmoothingSpinBox->setValue(m_SegmentationPreferencesNode->GetDouble("smoothing value", 1.0));
m_DecimationSpinBox->setValue(m_SegmentationPreferencesNode->GetDouble("decimation rate", 0.5));
m_ClosingSpinBox->setValue(m_SegmentationPreferencesNode->GetDouble("closing ratio", 0.0));
}
void QmitkSegmentationPreferencePage::OnSmoothingCheckboxChecked(int state)
{
if (state != Qt::Unchecked)
m_SmoothingSpinBox->setDisabled(true);
else
m_SmoothingSpinBox->setEnabled(true);
}
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.h b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.h
index 5b38f81271..5f816a7178 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.h
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.h
@@ -1,70 +1,68 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
-
-#ifndef QmitkSegmentationPreferencePage_h_included
-#define QmitkSegmentationPreferencePage_h_included
+#ifndef QMITKSEGMENTATIONPREFERENCEPAGE_H
+#define QMITKSEGMENTATIONPREFERENCEPAGE_H
#include "org_mitk_gui_qt_segmentation_Export.h"
+
#include <berryIPreferences.h>
-#include "berryIQtPreferencePage.h"
+#include <berryIQtPreferencePage.h>
-class QWidget;
class QCheckBox;
class QRadioButton;
class QDoubleSpinBox;
class MITK_QT_SEGMENTATION QmitkSegmentationPreferencePage : public QObject, public berry::IQtPreferencePage
{
Q_OBJECT
Q_INTERFACES(berry::IPreferencePage)
public:
QmitkSegmentationPreferencePage();
~QmitkSegmentationPreferencePage() override;
void Init(berry::IWorkbench::Pointer workbench) override;
void CreateQtControl(QWidget* widget) override;
QWidget* GetQtControl() const override;
bool PerformOk() override;
void PerformCancel() override;
void Update() override;
-protected slots:
+protected Q_SLOTS:
void OnSmoothingCheckboxChecked(int);
protected:
QWidget* m_MainControl;
QCheckBox* m_SlimViewCheckBox;
QRadioButton* m_RadioOutline;
QRadioButton* m_RadioOverlay;
QCheckBox* m_SelectionModeCheckBox;
QCheckBox* m_SmoothingCheckBox;
QDoubleSpinBox* m_SmoothingSpinBox;
QDoubleSpinBox* m_DecimationSpinBox;
QDoubleSpinBox* m_ClosingSpinBox;
bool m_Initializing;
berry::IPreferences::Pointer m_SegmentationPreferencesNode;
};
-#endif /* QMITKDATAMANAGERPREFERENCEPAGE_H_ */
-
+#endif // QMITKSEGMENTATIONPREFERENCEPAGE_H
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLabelsWidget.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLabelsWidget.cpp
new file mode 100644
index 0000000000..3345f642f1
--- /dev/null
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLabelsWidget.cpp
@@ -0,0 +1,173 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#include "QmitkLabelsWidget.h"
+#include <ui_QmitkLabelsWidgetControls.h>
+
+// mitk
+#include <mitkDataNode.h>
+#include <mitkImage.h>
+#include <mitkLabelSetImage.h>
+#include <mitkLabelSetImageHelper.h>
+#include <mitkToolManagerProvider.h>
+
+// Qmitk
+#include <QmitkAbstractNodeSelectionWidget.h>
+#include <QmitkStyleManager.h>
+
+#include "../QmitkSaveMultiLabelPresetAction.h"
+#include "../QmitkLoadMultiLabelPresetAction.h"
+
+// Qt
+#include <QMessageBox>
+
+QmitkLabelsWidget::QmitkLabelsWidget(QWidget *parent)
+ : QWidget(parent)
+ , m_Controls(new Ui::QmitkLabelsWidgetControls)
+ , m_ToolManager(nullptr)
+{
+ m_Controls->setupUi(this);
+
+ m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager();
+
+ m_Controls->savePresetButton->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-save.svg")));
+ m_Controls->loadPresetButton->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-open.svg")));
+
+ connect(m_Controls->newLabelButton, &QToolButton::clicked, this, &QmitkLabelsWidget::OnNewLabel);
+ connect(m_Controls->lockExteriorButton, &QToolButton::toggled, this, &QmitkLabelsWidget::OnLockExterior);
+ connect(m_Controls->savePresetButton, &QToolButton::clicked, this, &QmitkLabelsWidget::OnSavePreset);
+ connect(m_Controls->loadPresetButton, &QToolButton::clicked, this, &QmitkLabelsWidget::OnLoadPreset);
+ connect(m_Controls->showLabelTableButton, &QToolButton::toggled, this, &QmitkLabelsWidget::ShowLabelTable);
+
+ this->UpdateGUI();
+}
+
+QmitkLabelsWidget::~QmitkLabelsWidget()
+{
+ delete m_Controls;
+}
+
+void QmitkLabelsWidget::UpdateGUI()
+{
+ m_Controls->newLabelButton->setEnabled(false);
+ m_Controls->lockExteriorButton->setEnabled(false);
+ m_Controls->lockExteriorButton->setChecked(false);
+ m_Controls->savePresetButton->setEnabled(false);
+ m_Controls->loadPresetButton->setEnabled(false);
+ m_Controls->showLabelTableButton->setEnabled(false);
+ m_Controls->showLabelTableButton->setChecked(false);
+
+ mitk::LabelSetImage* workingImage = this->GetWorkingImage();
+ if (nullptr == workingImage)
+ {
+ return;
+ }
+
+ int activeLayer = workingImage->GetActiveLayer();
+ m_Controls->lockExteriorButton->setEnabled(true);
+ m_Controls->lockExteriorButton->setChecked(workingImage->GetLabel(0, activeLayer)->GetLocked());
+ m_Controls->showLabelTableButton->setEnabled(true);
+ m_Controls->showLabelTableButton->setChecked(true);
+ m_Controls->newLabelButton->setEnabled(true);
+ m_Controls->savePresetButton->setEnabled(true);
+ m_Controls->loadPresetButton->setEnabled(true);
+}
+
+mitk::LabelSetImage* QmitkLabelsWidget::GetWorkingImage()
+{
+ mitk::DataNode* workingNode = this->GetWorkingNode();
+ if (nullptr == workingNode)
+ {
+ return nullptr;
+ }
+
+ auto workingImage = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
+ return workingImage;
+}
+
+mitk::DataNode* QmitkLabelsWidget::GetWorkingNode()
+{
+ mitk::DataNode* referenceNode = m_ToolManager->GetWorkingData(0);
+ return referenceNode;
+}
+
+void QmitkLabelsWidget::OnNewLabel()
+{
+ m_ToolManager->ActivateTool(-1);
+
+ mitk::DataNode* workingNode = this->GetWorkingNode();
+ if (nullptr == workingNode)
+ {
+ return;
+ }
+
+ auto workingImage = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
+ if (nullptr == workingImage)
+ {
+ return;
+ }
+
+ this->WaitCursorOn();
+ mitk::Label::Pointer newLabel = mitk::LabelSetImageHelper::CreateNewLabel(workingImage);
+ workingImage->GetActiveLabelSet()->AddLabel(newLabel);
+ this->WaitCursorOff();
+
+ this->UpdateGUI();
+ emit LabelsChanged();
+}
+
+void QmitkLabelsWidget::OnLockExterior(bool checked)
+{
+ auto workingImage = this->GetWorkingImage();
+ if (nullptr == workingImage)
+ {
+ return;
+ }
+
+ workingImage->GetLabel(0)->SetLocked(checked);
+}
+
+void QmitkLabelsWidget::OnSavePreset()
+{
+ auto workingNode = this->GetWorkingNode();
+ QmitkAbstractNodeSelectionWidget::NodeList nodes;
+ nodes.append(workingNode);
+
+ QmitkSaveMultiLabelPresetAction action;
+ action.Run(nodes);
+}
+
+void QmitkLabelsWidget::OnLoadPreset()
+{
+ auto workingNode = this->GetWorkingNode();
+ QmitkAbstractNodeSelectionWidget::NodeList nodes;
+ nodes.append(workingNode);
+
+ QmitkLoadMultiLabelPresetAction action;
+ action.Run(nodes);
+}
+
+void QmitkLabelsWidget::WaitCursorOn()
+{
+ QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+}
+
+void QmitkLabelsWidget::WaitCursorOff()
+{
+ this->RestoreOverrideCursor();
+}
+
+void QmitkLabelsWidget::RestoreOverrideCursor()
+{
+ QApplication::restoreOverrideCursor();
+}
+
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLabelsWidget.h b/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLabelsWidget.h
new file mode 100644
index 0000000000..bd74ae2ead
--- /dev/null
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLabelsWidget.h
@@ -0,0 +1,82 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#ifndef QMITKLABELSWIDGET_H
+#define QMITKLABELSWIDGET_H
+
+// mitk core
+#include <mitkWeakPointer.h>
+
+// Qt
+#include <QWidget>
+
+namespace Ui
+{
+ class QmitkLabelsWidgetControls;
+}
+
+namespace mitk
+{
+ class DataNode;
+ class Image;
+ class LabelSetImage;
+ class ToolManager;
+}
+
+class QmitkLabelsWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+
+ explicit QmitkLabelsWidget(QWidget* parent = nullptr);
+ ~QmitkLabelsWidget() override;
+
+ void UpdateGUI();
+
+Q_SIGNALS:
+
+ void LabelsChanged();
+
+ void ShowLabelTable(bool);
+
+private:
+
+ mitk::LabelSetImage* GetWorkingImage();
+
+ mitk::DataNode* GetWorkingNode();
+
+ // reaction to button "New Label"
+ void OnNewLabel();
+
+ // reaction to the button "Lock exterior"
+ void OnLockExterior(bool);
+
+ // reaction to button "Save Preset"
+ void OnSavePreset();
+
+ // reaction to button "Load Preset"
+ void OnLoadPreset();
+
+ void WaitCursorOn();
+
+ void WaitCursorOff();
+
+ void RestoreOverrideCursor();
+
+ Ui::QmitkLabelsWidgetControls* m_Controls;
+
+ mitk::ToolManager* m_ToolManager;
+
+};
+
+#endif
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLabelsWidgetControls.ui b/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLabelsWidgetControls.ui
new file mode 100644
index 0000000000..35fb68f86b
--- /dev/null
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLabelsWidgetControls.ui
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QmitkLabelsWidgetControls</class>
+ <widget class="QWidget" name="QmitkLabelsWidgetControls">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>300</width>
+ <height>75</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="groupBox_Labels">
+ <property name="title">
+ <string>Labels</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QToolButton" name="newLabelButton">
+ <property name="toolTip">
+ <string>Add a label to the current segmentation session</string>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset>
+ <normaloff>:/Qmitk/NewLabel_48x48.png</normaloff>:/Qmitk/NewLabel_48x48.png</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>28</width>
+ <height>28</height>
+ </size>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="lockExteriorButton">
+ <property name="toolTip">
+ <string>Lock / unlock exterior</string>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset>
+ <normaloff>:/Qmitk/UnlockExterior_48x48.png</normaloff>
+ <normalon>:/Qmitk/LockExterior_48x48.png</normalon>:/Qmitk/UnlockExterior_48x48.png</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>28</width>
+ <height>28</height>
+ </size>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="savePresetButton">
+ <property name="toolTip">
+ <string>Save labelset preset</string>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset>
+ <normaloff>:/org_mitk_icons/icons/awesome/scalable/actions/document-save.svg</normaloff>:/org_mitk_icons/icons/awesome/scalable/actions/document-save.svg</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>28</width>
+ <height>28</height>
+ </size>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="loadPresetButton">
+ <property name="toolTip">
+ <string>Load LabelSet Preset</string>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset>
+ <normaloff>:/org_mitk_icons/icons/awesome/scalable/actions/document-open.svg</normaloff>:/org_mitk_icons/icons/awesome/scalable/actions/document-open.svg</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>28</width>
+ <height>28</height>
+ </size>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QToolButton" name="showLabelTableButton">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>34</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Show a table with all labels in the current segmentation session</string>
+ </property>
+ <property name="text">
+ <string>&gt;&gt;</string>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>28</width>
+ <height>28</height>
+ </size>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="autoRaise">
+ <bool>false</bool>
+ </property>
+ <property name="arrowType">
+ <enum>Qt::NoArrow</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLayersWidget.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLayersWidget.cpp
new file mode 100644
index 0000000000..fc0d43bdf8
--- /dev/null
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLayersWidget.cpp
@@ -0,0 +1,237 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#include "QmitkLayersWidget.h"
+#include <ui_QmitkLayersWidgetControls.h>
+
+// mitk
+#include <mitkDataNode.h>
+#include <mitkLabelSetImage.h>
+#include <mitkToolManagerProvider.h>
+
+// Qt
+#include <QMessageBox>
+
+QmitkLayersWidget::QmitkLayersWidget(QWidget *parent)
+ : QWidget(parent)
+ , m_Controls(new Ui::QmitkLayersWidgetControls)
+ , m_ToolManager(nullptr)
+{
+ m_Controls->setupUi(this);
+
+ m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager();
+
+ connect(m_Controls->addLayerButton, &QToolButton::clicked, this, &QmitkLayersWidget::OnAddLayer);
+ connect(m_Controls->deleteLayerButton, &QToolButton::clicked, this, &QmitkLayersWidget::OnDeleteLayer);
+ connect(m_Controls->previousLayerButton, &QToolButton::clicked, this, &QmitkLayersWidget::OnPreviousLayer);
+ connect(m_Controls->nextLayerButton, &QToolButton::clicked, this, &QmitkLayersWidget::OnNextLayer);
+ connect(m_Controls->activeLayerComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &QmitkLayersWidget::OnChangeLayer);
+
+ this->UpdateGUI();
+}
+
+QmitkLayersWidget::~QmitkLayersWidget()
+{
+ delete m_Controls;
+}
+
+void QmitkLayersWidget::UpdateGUI()
+{
+ m_Controls->addLayerButton->setEnabled(false);
+ m_Controls->deleteLayerButton->setEnabled(false);
+ m_Controls->previousLayerButton->setEnabled(false);
+ m_Controls->nextLayerButton->setEnabled(false);
+ m_Controls->activeLayerComboBox->setEnabled(false);
+
+ mitk::LabelSetImage* workingImage = this->GetWorkingImage();
+ if (nullptr == workingImage)
+ {
+ return;
+ }
+
+ m_Controls->addLayerButton->setEnabled(true);
+
+ m_Controls->activeLayerComboBox->blockSignals(true);
+ m_Controls->activeLayerComboBox->clear();
+
+ unsigned int numberOfLayers = workingImage->GetNumberOfLayers();
+ for (unsigned int lidx = 0; lidx < numberOfLayers; ++lidx)
+ {
+ m_Controls->activeLayerComboBox->addItem(QString::number(lidx));
+ }
+
+ unsigned int activeLayer = workingImage->GetActiveLayer();
+ m_Controls->activeLayerComboBox->setCurrentIndex(activeLayer);
+ m_Controls->activeLayerComboBox->blockSignals(false);
+
+ m_Controls->deleteLayerButton->setEnabled(numberOfLayers > 1);
+ m_Controls->previousLayerButton->setEnabled(activeLayer > 0);
+ m_Controls->nextLayerButton->setEnabled(activeLayer != numberOfLayers - 1);
+ m_Controls->activeLayerComboBox->setEnabled(numberOfLayers > 0);
+}
+
+mitk::LabelSetImage* QmitkLayersWidget::GetWorkingImage()
+{
+ mitk::DataNode* workingNode = GetWorkingNode();
+ if (nullptr == workingNode)
+ {
+ return nullptr;
+ }
+
+ auto workingImage = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
+ return workingImage;
+}
+
+mitk::DataNode* QmitkLayersWidget::GetWorkingNode()
+{
+ mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0);
+ return workingNode;
+}
+
+void QmitkLayersWidget::OnAddLayer()
+{
+ m_ToolManager->ActivateTool(-1);
+
+ auto workingImage = this->GetWorkingImage();
+ if (nullptr == workingImage)
+ {
+ return;
+ }
+
+ try
+ {
+ this->WaitCursorOn();
+ workingImage->AddLayer();
+ this->WaitCursorOff();
+ }
+ catch (mitk::Exception& e)
+ {
+ this->WaitCursorOff();
+ MITK_ERROR << "Exception caught: " << e.GetDescription();
+ QMessageBox::information(
+ this, "Add layer", "Could not add a new layer. See error log for details.\n");
+ return;
+ }
+
+ this->UpdateGUI();
+ emit LayersChanged();
+}
+
+void QmitkLayersWidget::OnDeleteLayer()
+{
+ m_ToolManager->ActivateTool(-1);
+
+ auto workingImage = this->GetWorkingImage();
+ if (nullptr == workingImage)
+ {
+ return;
+ }
+
+ if (workingImage->GetNumberOfLayers() < 2)
+ {
+ return;
+ }
+
+ QString question = "Do you really want to delete the current layer?";
+ QMessageBox::StandardButton answerButton = QMessageBox::question(
+ this, "Delete layer", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
+
+ if (answerButton != QMessageBox::Yes)
+ {
+ return;
+ }
+
+ try
+ {
+ this->WaitCursorOn();
+ workingImage->RemoveLayer();
+ this->WaitCursorOff();
+ }
+ catch (mitk::Exception& e)
+ {
+ this->WaitCursorOff();
+ MITK_ERROR << "Exception caught: " << e.GetDescription();
+ QMessageBox::information(
+ this, "Delete layer", "Could not delete the currently active layer. See error log for details.\n");
+ return;
+ }
+
+ this->UpdateGUI();
+ emit LayersChanged();
+}
+
+void QmitkLayersWidget::OnPreviousLayer()
+{
+ auto workingImage = this->GetWorkingImage();
+ if (nullptr == workingImage)
+ {
+ return;
+ }
+
+ this->OnChangeLayer(workingImage->GetActiveLayer() - 1);
+}
+
+void QmitkLayersWidget::OnNextLayer()
+{
+ auto workingImage = this->GetWorkingImage();
+ if (nullptr == workingImage)
+ {
+ return;
+ }
+
+ this->OnChangeLayer(workingImage->GetActiveLayer() + 1);
+}
+
+void QmitkLayersWidget::OnChangeLayer(int layer)
+{
+ m_ToolManager->ActivateTool(-1);
+
+ auto workingImage = this->GetWorkingImage();
+ if (nullptr == workingImage)
+ {
+ return;
+ }
+
+ try
+ {
+ this->WaitCursorOn();
+ workingImage->SetActiveLayer(layer);
+ this->WaitCursorOff();
+ }
+ catch (mitk::Exception& e)
+ {
+ this->WaitCursorOff();
+ MITK_ERROR << "Exception caught: " << e.GetDescription();
+ QMessageBox::information(
+ this, "Change layer", "Could not change the layer. See error log for details.\n");
+ return;
+ }
+
+ this->UpdateGUI();
+ emit LayersChanged();
+}
+
+void QmitkLayersWidget::WaitCursorOn()
+{
+ QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+}
+
+void QmitkLayersWidget::WaitCursorOff()
+{
+ this->RestoreOverrideCursor();
+}
+
+void QmitkLayersWidget::RestoreOverrideCursor()
+{
+ QApplication::restoreOverrideCursor();
+}
+
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLayersWidget.h b/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLayersWidget.h
new file mode 100644
index 0000000000..654e154968
--- /dev/null
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLayersWidget.h
@@ -0,0 +1,79 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+All rights reserved.
+
+Use of this source code is governed by a 3-clause BSD license that can be
+found in the LICENSE file.
+
+============================================================================*/
+
+#ifndef QMITKLAYERSWIDGET_H
+#define QMITKLAYERSWIDGET_H
+
+// Qt
+#include <QWidget>
+
+namespace Ui
+{
+ class QmitkLayersWidgetControls;
+}
+
+namespace mitk
+{
+ class DataNode;
+ class LabelSetImage;
+ class ToolManager;
+}
+
+class QmitkLayersWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+
+ explicit QmitkLayersWidget(QWidget* parent = nullptr);
+ ~QmitkLayersWidget() override;
+
+ void UpdateGUI();
+
+Q_SIGNALS:
+
+ void LayersChanged();
+
+private:
+
+ mitk::LabelSetImage* GetWorkingImage();
+
+ mitk::DataNode* GetWorkingNode();
+
+ // reaction to the button "Add Layer"
+ void OnAddLayer();
+
+ // reaction to the button "Delete Layer"
+ void OnDeleteLayer();
+
+ // reaction to the button "Previous Layer"
+ void OnPreviousLayer();
+
+ // reaction to the button "Next Layer"
+ void OnNextLayer();
+
+ // reaction to the combobox change "Change Layer"
+ void OnChangeLayer(int);
+
+ void WaitCursorOn();
+
+ void WaitCursorOff();
+
+ void RestoreOverrideCursor();
+
+ Ui::QmitkLayersWidgetControls* m_Controls;
+
+ mitk::ToolManager* m_ToolManager;
+
+};
+
+#endif
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLayersWidgetControls.ui b/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLayersWidgetControls.ui
new file mode 100644
index 0000000000..f2941e4af5
--- /dev/null
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLayersWidgetControls.ui
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QmitkLayersWidgetControls</class>
+ <widget class="QWidget" name="QmitkLayersWidgetControls">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>300</width>
+ <height>75</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="groupBox_Layer">
+ <property name="title">
+ <string>Layers</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QToolButton" name="addLayerButton">
+ <property name="toolTip">
+ <string>Add a layer to the current segmentation session</string>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset>
+ <normaloff>:/Qmitk/AddLayer_48x48.png</normaloff>:/Qmitk/AddLayer_48x48.png</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>28</width>
+ <height>28</height>
+ </size>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="deleteLayerButton">
+ <property name="toolTip">
+ <string>Delete the active layer</string>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset>
+ <normaloff>:/Qmitk/DeleteLayer_48x48.png</normaloff>:/Qmitk/DeleteLayer_48x48.png</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>28</width>
+ <height>28</height>
+ </size>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QToolButton" name="previousLayerButton">
+ <property name="toolTip">
+ <string>Change to the previous available layer</string>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset>
+ <normaloff>:/Qmitk/PreviousLayer_48x48.png</normaloff>:/Qmitk/PreviousLayer_48x48.png</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>28</width>
+ <height>28</height>
+ </size>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="nextLayerButton">
+ <property name="toolTip">
+ <string>Change to the next available layer</string>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset>
+ <normaloff>:/Qmitk/NextLayer_48x48.png</normaloff>:/Qmitk/NextLayer_48x48.png</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>28</width>
+ <height>28</height>
+ </size>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="activeLayerComboBox">
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>30</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>40</width>
+ <height>30</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Switch to a layer</string>
+ </property>
+ <item>
+ <property name="text">
+ <string>0</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp
similarity index 100%
rename from Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp
rename to Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkLoadMultiLabelPresetAction.h b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkLoadMultiLabelPresetAction.h
similarity index 100%
rename from Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkLoadMultiLabelPresetAction.h
rename to Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkLoadMultiLabelPresetAction.h
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelPresetAction.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSaveMultiLabelPresetAction.cpp
similarity index 75%
rename from Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelPresetAction.cpp
rename to Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSaveMultiLabelPresetAction.cpp
index 5c245ed824..b813c809e8 100644
--- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelPresetAction.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSaveMultiLabelPresetAction.cpp
@@ -1,63 +1,63 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
-#include "QmitkCreateMultiLabelPresetAction.h"
+#include "QmitkSaveMultiLabelPresetAction.h"
#include <mitkLabelSetImage.h>
#include <mitkLabelSetIOHelper.h>
#include <QFileDialog>
#include <QMessageBox>
-void QmitkCreateMultiLabelPresetAction::Run(const QList<mitk::DataNode::Pointer> &selectedNodes)
+void QmitkSaveMultiLabelPresetAction::Run(const QList<mitk::DataNode::Pointer> &selectedNodes)
{
for (const auto &node : selectedNodes)
{
if (node.IsNull())
continue;
mitk::LabelSetImage::Pointer image = dynamic_cast<mitk::LabelSetImage*>(node->GetData());
if (image.IsNull())
continue;
const auto filename = QFileDialog::getSaveFileName(nullptr, QStringLiteral("Save LabelSet Preset"),
QString(), QStringLiteral("LabelSet Preset (*.lsetp)")).toStdString();
if (filename.empty())
continue;
if(!mitk::LabelSetIOHelper::SaveLabelSetImagePreset(filename, image))
{
QMessageBox::critical(nullptr, QStringLiteral("Save LabelSetImage Preset"),
QString("Could not save \"%1\" as preset.").arg(QString::fromStdString(node->GetName())));
continue;
}
}
}
-void QmitkCreateMultiLabelPresetAction::SetDataStorage(mitk::DataStorage*)
+void QmitkSaveMultiLabelPresetAction::SetDataStorage(mitk::DataStorage*)
{
}
-void QmitkCreateMultiLabelPresetAction::SetFunctionality(berry::QtViewPart*)
+void QmitkSaveMultiLabelPresetAction::SetFunctionality(berry::QtViewPart*)
{
}
-void QmitkCreateMultiLabelPresetAction::SetSmoothed(bool)
+void QmitkSaveMultiLabelPresetAction::SetSmoothed(bool)
{
}
-void QmitkCreateMultiLabelPresetAction::SetDecimated(bool)
+void QmitkSaveMultiLabelPresetAction::SetDecimated(bool)
{
}
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelPresetAction.h b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSaveMultiLabelPresetAction.h
similarity index 69%
rename from Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelPresetAction.h
rename to Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSaveMultiLabelPresetAction.h
index 2406a22bd8..0b948cc46e 100644
--- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelPresetAction.h
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSaveMultiLabelPresetAction.h
@@ -1,34 +1,34 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
-#ifndef QmitkCreateMultiLabelPresetAction_h
-#define QmitkCreateMultiLabelPresetAction_h
+#ifndef QMITKSAVEMULTILABELPRESETACTION_H
+#define QMITKSAVEMULTILABELPRESETACTION_H
#include <mitkIContextMenuAction.h>
-class QmitkCreateMultiLabelPresetAction : public QObject, public mitk::IContextMenuAction
+class QmitkSaveMultiLabelPresetAction : public QObject, public mitk::IContextMenuAction
{
Q_OBJECT
Q_INTERFACES(mitk::IContextMenuAction)
public:
- QmitkCreateMultiLabelPresetAction() = default;
- ~QmitkCreateMultiLabelPresetAction() override = default;
+ QmitkSaveMultiLabelPresetAction() = default;
+ ~QmitkSaveMultiLabelPresetAction() override = default;
void Run(const QList<mitk::DataNode::Pointer>& selectedNodes) override;
void SetDataStorage(mitk::DataStorage*) override;
void SetFunctionality(berry::QtViewPart*) override;
void SetSmoothed(bool) override;
void SetDecimated(bool) override;
};
-#endif
+#endif // QMITKSAVEMULTILABELPRESETACTION_H
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp
index b26097ebce..a2f7c485ce 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp
@@ -1,809 +1,983 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "QmitkSegmentationView.h"
#include "mitkPluginActivator.h"
// blueberry
#include <berryIWorkbenchPage.h>
// mitk
#include <mitkApplicationCursor.h>
#include <mitkCameraController.h>
#include <mitkImageTimeSelector.h>
#include <mitkLabelSetImage.h>
+#include <mitkLabelSetImageHelper.h>
#include <mitkNodePredicateSubGeometry.h>
#include <mitkSegmentationObjectFactory.h>
#include <mitkSegTool2D.h>
#include <mitkStatusBar.h>
#include <mitkToolManagerProvider.h>
#include <mitkVtkResliceInterpolationProperty.h>
#include <mitkWorkbenchUtil.h>
// Qmitk
-#include <QmitkNewSegmentationDialog.h>
#include <QmitkRenderWindow.h>
#include <QmitkSegmentationOrganNamesHandling.cpp>
// us
#include <usModuleResource.h>
#include <usModuleResourceStream.h>
// Qt
#include <QMessageBox>
+#include <QShortcut>
#include <regex>
const std::string QmitkSegmentationView::VIEW_ID = "org.mitk.views.segmentation";
QmitkSegmentationView::QmitkSegmentationView()
: m_Parent(nullptr)
, m_Controls(nullptr)
, m_RenderWindowPart(nullptr)
, m_ToolManager(nullptr)
, m_ReferenceNode(nullptr)
, m_WorkingNode(nullptr)
- , m_AutoSelectionEnabled(false)
+ , m_DrawOutline(true)
+ , m_SelectionMode(false)
, m_MouseCursorSet(false)
{
- mitk::TNodePredicateDataType<mitk::Image>::Pointer isImage = mitk::TNodePredicateDataType<mitk::Image>::New();
+ auto isImage = mitk::TNodePredicateDataType<mitk::Image>::New();
auto isDwi = mitk::NodePredicateDataType::New("DiffusionImage");
auto isDti = mitk::NodePredicateDataType::New("TensorImage");
auto isOdf = mitk::NodePredicateDataType::New("OdfImage");
auto isSegment = mitk::NodePredicateDataType::New("Segment");
- mitk::NodePredicateOr::Pointer validImages = mitk::NodePredicateOr::New();
+ auto validImages = mitk::NodePredicateOr::New();
validImages->AddPredicate(mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateNot::New(isSegment)));
validImages->AddPredicate(isDwi);
validImages->AddPredicate(isDti);
validImages->AddPredicate(isOdf);
auto isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true));
auto isMask = mitk::NodePredicateAnd::New(isBinary, isImage);
auto validSegmentations = mitk::NodePredicateOr::New();
validSegmentations->AddPredicate(mitk::TNodePredicateDataType<mitk::LabelSetImage>::New());
validSegmentations->AddPredicate(isMask);
m_SegmentationPredicate = mitk::NodePredicateAnd::New();
m_SegmentationPredicate->AddPredicate(validSegmentations);
m_SegmentationPredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")));
m_SegmentationPredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("hidden object")));
m_ReferencePredicate = mitk::NodePredicateAnd::New();
m_ReferencePredicate->AddPredicate(validImages);
m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(m_SegmentationPredicate));
m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")));
m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("hidden object")));
}
QmitkSegmentationView::~QmitkSegmentationView()
{
- if (m_Controls)
+ if (nullptr != m_Controls)
{
+ OnLooseLabelSetConnection();
+
// deactivate all tools
m_ToolManager->ActivateTool(-1);
// removing all observers
for (NodeTagMapType::iterator dataIter = m_WorkingDataObserverTags.begin(); dataIter != m_WorkingDataObserverTags.end(); ++dataIter)
{
(*dataIter).first->GetProperty("visible")->RemoveObserver((*dataIter).second);
}
m_WorkingDataObserverTags.clear();
mitk::RenderingManager::GetInstance()->RemoveObserver(m_RenderingManagerObserverTag);
ctkPluginContext* context = mitk::PluginActivator::getContext();
ctkServiceReference ppmRef = context->getServiceReference<mitk::PlanePositionManagerService>();
mitk::PlanePositionManagerService* service = context->getService<mitk::PlanePositionManagerService>(ppmRef);
service->RemoveAllPlanePositions();
context->ungetService(ppmRef);
m_ToolManager->SetReferenceData(nullptr);
m_ToolManager->SetWorkingData(nullptr);
}
delete m_Controls;
}
/**********************************************************************/
/* private Q_SLOTS */
/**********************************************************************/
void QmitkSegmentationView::OnReferenceSelectionChanged(QList<mitk::DataNode::Pointer> nodes)
{
m_ToolManager->ActivateTool(-1);
if (nodes.empty())
{
- m_Controls->segImageSelector->SetNodePredicate(m_SegmentationPredicate);
+ m_Controls->workingNodeSelector->SetNodePredicate(m_SegmentationPredicate);
m_ReferenceNode = nullptr;
m_ToolManager->SetReferenceData(m_ReferenceNode);
this->UpdateGUI();
return;
}
m_ReferenceNode = nodes.first();
m_ToolManager->SetReferenceData(m_ReferenceNode);
if (m_ReferenceNode.IsNotNull())
{
// set a predicate such that a segmentation fits the selected reference image geometry
auto segPredicate = mitk::NodePredicateAnd::New(m_SegmentationPredicate.GetPointer(),
mitk::NodePredicateSubGeometry::New(m_ReferenceNode->GetData()->GetGeometry()));
- m_Controls->segImageSelector->SetNodePredicate(segPredicate);
+ m_Controls->workingNodeSelector->SetNodePredicate(segPredicate);
- if (m_AutoSelectionEnabled)
+ if (m_SelectionMode)
{
// hide all image nodes to later show only the automatically selected ones
mitk::DataStorage::SetOfObjects::ConstPointer imageNodes =
this->GetDataStorage()->GetSubset(m_ReferencePredicate);
for (mitk::DataStorage::SetOfObjects::const_iterator iter = imageNodes->begin(); iter != imageNodes->end(); ++iter)
{
(*iter)->SetVisibility(false);
}
}
m_ReferenceNode->SetVisibility(true);
}
this->UpdateGUI();
}
void QmitkSegmentationView::OnSegmentationSelectionChanged(QList<mitk::DataNode::Pointer> nodes)
{
m_ToolManager->ActivateTool(-1);
// Remove observer if one was registered
auto finding = m_WorkingDataObserverTags.find(m_WorkingNode);
if (finding != m_WorkingDataObserverTags.end())
{
m_WorkingNode->GetProperty("visible")->RemoveObserver(m_WorkingDataObserverTags[m_WorkingNode]);
m_WorkingDataObserverTags.erase(m_WorkingNode);
}
if (nodes.empty())
{
m_WorkingNode = nullptr;
m_ToolManager->SetWorkingData(m_WorkingNode);
this->UpdateGUI();
return;
}
if (m_ReferenceNode.IsNull())
{
this->UpdateGUI();
return;
}
- mitk::Image::ConstPointer referenceImage = dynamic_cast<mitk::Image *>(m_ReferenceNode->GetData());
+ mitk::Image::ConstPointer referenceImage = dynamic_cast<mitk::Image*>(m_ReferenceNode->GetData());
if (referenceImage.IsNull())
{
this->UpdateGUI();
return;
}
m_WorkingNode = nodes.first();
m_ToolManager->SetWorkingData(m_WorkingNode);
if (m_WorkingNode.IsNotNull())
{
- if (m_AutoSelectionEnabled)
+ if (m_SelectionMode)
{
- // hide all segmentation nodes to later show only the automatically selected ones
+ // hide all segmentation nodes to later show only the selected ones
mitk::DataStorage::SetOfObjects::ConstPointer segmentationNodes =
this->GetDataStorage()->GetSubset(m_SegmentationPredicate);
for (mitk::DataStorage::SetOfObjects::const_iterator iter = segmentationNodes->begin(); iter != segmentationNodes->end(); ++iter)
{
(*iter)->SetVisibility(false);
}
}
m_WorkingNode->SetVisibility(true);
+ this->OnEstablishLabelSetConnection();
+ m_Controls->labelSetWidget->ResetAllTableWidgetItems();
+
auto command = itk::SimpleMemberCommand<QmitkSegmentationView>::New();
command->SetCallbackFunction(this, &QmitkSegmentationView::ValidateSelectionInput);
- m_WorkingDataObserverTags.insert(std::pair<mitk::DataNode *, unsigned long>(m_WorkingNode,
+ m_WorkingDataObserverTags.insert(std::pair<mitk::DataNode*, unsigned long>(m_WorkingNode,
m_WorkingNode->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command)));
this->InitializeRenderWindows(referenceImage->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, false);
}
this->UpdateGUI();
}
-void QmitkSegmentationView::CreateNewSegmentation()
+void QmitkSegmentationView::OnVisibilityShortcutActivated()
+{
+ if (m_WorkingNode.IsNull())
+ {
+ return;
+ }
+
+ bool isVisible = false;
+ m_WorkingNode->GetBoolProperty("visible", isVisible);
+ m_WorkingNode->SetVisibility(!isVisible);
+
+ mitk::RenderingManager::GetInstance()->RequestUpdateAll();
+}
+
+void QmitkSegmentationView::OnLabelToggleShortcutActivated()
{
- mitk::DataNode::Pointer referenceNode = m_ToolManager->GetReferenceData(0);
- if (referenceNode.IsNull())
+ if (m_WorkingNode.IsNull())
+ {
+ return;
+ }
+
+ auto workingImage = dynamic_cast<mitk::LabelSetImage*>(m_WorkingNode->GetData());
+ if (nullptr == workingImage)
+ {
+ return;
+ }
+
+ this->WaitCursorOn();
+ workingImage->GetActiveLabelSet()->SetNextActiveLabel();
+ workingImage->Modified();
+ this->WaitCursorOff();
+
+ mitk::RenderingManager::GetInstance()->RequestUpdateAll();
+}
+
+void QmitkSegmentationView::OnNewSegmentation()
+{
+ m_ToolManager->ActivateTool(-1);
+
+ if (m_ReferenceNode.IsNull())
{
MITK_ERROR << "'Create new segmentation' button should never be clickable unless a reference image is selected.";
return;
}
- mitk::Image::ConstPointer referenceImage = dynamic_cast<mitk::Image *>(referenceNode->GetData());
+ mitk::Image::ConstPointer referenceImage = dynamic_cast<mitk::Image*>(m_ReferenceNode->GetData());
if (referenceImage.IsNull())
{
QMessageBox::information(
m_Parent, "New segmentation", "Please load and select an image before starting some action.");
return;
}
if (referenceImage->GetDimension() <= 1)
{
- QMessageBox::information(m_Parent, "New segmentation", "Segmentation is currently not supported for 2D images");
+ QMessageBox::information(
+ m_Parent, "New segmentation", "Segmentation is currently not supported for 2D images");
return;
}
- m_ToolManager->ActivateTool(-1);
-
const auto currentTimePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint();
unsigned int imageTimeStep = 0;
if (referenceImage->GetTimeGeometry()->IsValidTimePoint(currentTimePoint))
{
imageTimeStep = referenceImage->GetTimeGeometry()->TimePointToTimeStep(currentTimePoint);
}
auto segTemplateImage = referenceImage;
if (referenceImage->GetDimension() > 3)
{
auto result = QMessageBox::question(m_Parent,
tr("Create a static or dynamic segmentation?"),
tr("The selected image has multiple time steps.\n\nDo you want to create a static "
"segmentation that is identical for all time steps or do you want to create a "
"dynamic segmentation to segment individual time steps?"),
tr("Create static segmentation"), tr("Create dynamic segmentation"),
QString(), 0, 0);
if (result == 0)
{
auto selector = mitk::ImageTimeSelector::New();
selector->SetInput(referenceImage);
selector->SetTimeNr(0);
selector->Update();
const auto refTimeGeometry = referenceImage->GetTimeGeometry();
auto newTimeGeometry = mitk::ProportionalTimeGeometry::New();
newTimeGeometry->SetFirstTimePoint(refTimeGeometry->GetMinimumTimePoint());
newTimeGeometry->SetStepDuration(refTimeGeometry->GetMaximumTimePoint() - refTimeGeometry->GetMinimumTimePoint());
mitk::Image::Pointer newImage = selector->GetOutput();
newTimeGeometry->SetTimeStepGeometry(referenceImage->GetGeometry(imageTimeStep), 0);
newImage->SetTimeGeometry(newTimeGeometry);
segTemplateImage = newImage;
}
}
- QString newName = QString::fromStdString(referenceNode->GetName());
- newName.append("-labels");
-
- // ask about the name and organ type of the new segmentation
- auto dialog = new QmitkNewSegmentationDialog(m_Parent);
- QStringList organColors = mitk::OrganNamesHandling::GetDefaultOrganColorString();
- dialog->SetSuggestionList(organColors);
- dialog->SetSegmentationName(newName);
-
- int dialogReturnValue = dialog->exec();
- if (dialogReturnValue == QDialog::Rejected)
+ mitk::DataNode::Pointer newSegmentationNode;
+ try
{
- return;
+ this->WaitCursorOn();
+ newSegmentationNode = mitk::LabelSetImageHelper::CreateNewSegmentationNode(m_ReferenceNode, segTemplateImage);
+ this->WaitCursorOff();
}
-
- std::string newNodeName = dialog->GetSegmentationName().toStdString();
- if (newNodeName.empty())
+ catch (mitk::Exception& e)
{
- newNodeName = "Unnamed";
+ this->WaitCursorOff();
+ MITK_ERROR << "Exception caught: " << e.GetDescription();
+ QMessageBox::warning(m_Parent, "New segmentation", "Could not create a new segmentation.");
+ return;
}
- // create a new image of the same dimensions and smallest possible pixel type
- auto firstTool = m_ToolManager->GetToolById(0);
- if (nullptr == firstTool)
+ auto newLabelSetImage = dynamic_cast<mitk::LabelSetImage*>(newSegmentationNode->GetData());
+ if (nullptr == newLabelSetImage)
{
+ // something went wrong
return;
}
- mitk::DataNode::Pointer emptySegmentation = nullptr;
- try
- {
- emptySegmentation = firstTool->CreateEmptySegmentationNode(segTemplateImage, newNodeName, dialog->GetColor());
- }
- catch (const std::bad_alloc &)
- {
- QMessageBox::warning(m_Parent, tr("New segmentation"), tr("Could not allocate memory for new segmentation"));
- }
+ mitk::Label::Pointer newLabel = mitk::LabelSetImageHelper::CreateNewLabel(newLabelSetImage);
+ newLabelSetImage->GetActiveLabelSet()->AddLabel(newLabel);
- if (nullptr == emptySegmentation)
+ if (!this->GetDataStorage()->Exists(newSegmentationNode))
{
- return; // could have been aborted by user
+ this->GetDataStorage()->Add(newSegmentationNode, m_ReferenceNode);
}
- // initialize "showVolume"-property to false to prevent recalculating the volume while working on the segmentation
- emptySegmentation->SetProperty("showVolume", mitk::BoolProperty::New(false));
-
- mitk::OrganNamesHandling::UpdateOrganList(organColors, dialog->GetSegmentationName(), dialog->GetColor());
-
- // escape ';' here (replace by '\;')
- QString stringForStorage = organColors.replaceInStrings(";", "\\;").join(";");
- MITK_DEBUG << "Will store: " << stringForStorage;
- this->GetPreferences()->Put("Organ-Color-List", stringForStorage);
- this->GetPreferences()->Flush();
-
- this->GetDataStorage()->Add(emptySegmentation, referenceNode);
-
if (m_ToolManager->GetWorkingData(0))
{
m_ToolManager->GetWorkingData(0)->SetSelected(false);
}
- emptySegmentation->SetSelected(true);
- m_Controls->segImageSelector->SetCurrentSelectedNode(emptySegmentation);
+
+ newSegmentationNode->SetSelected(true);
+ m_Controls->workingNodeSelector->SetCurrentSelectedNode(newSegmentationNode);
}
void QmitkSegmentationView::OnManualTool2DSelected(int id)
{
this->ResetMouseCursor();
mitk::StatusBar::GetInstance()->DisplayText("");
if (id >= 0)
{
std::string text = "Active Tool: \"";
text += m_ToolManager->GetToolById(id)->GetName();
text += "\"";
mitk::StatusBar::GetInstance()->DisplayText(text.c_str());
us::ModuleResource resource = m_ToolManager->GetToolById(id)->GetCursorIconResource();
this->SetMouseCursor(resource, 0, 0);
}
}
void QmitkSegmentationView::OnShowMarkerNodes(bool state)
{
mitk::SegTool2D::Pointer manualSegmentationTool;
unsigned int numberOfExistingTools = m_ToolManager->GetTools().size();
for (unsigned int i = 0; i < numberOfExistingTools; i++)
{
manualSegmentationTool = dynamic_cast<mitk::SegTool2D*>(m_ToolManager->GetToolById(i));
-
- if (manualSegmentationTool)
+ if (nullptr == manualSegmentationTool)
{
- if (state == true)
- {
- manualSegmentationTool->SetShowMarkerNodes(true);
- }
- else
- {
- manualSegmentationTool->SetShowMarkerNodes(false);
- }
+ continue;
}
+
+ manualSegmentationTool->SetShowMarkerNodes(state);
}
}
+void QmitkSegmentationView::OnLayersChanged()
+{
+ m_Controls->labelSetWidget->ResetAllTableWidgetItems();
+}
+
+void QmitkSegmentationView::OnLabelsChanged()
+{
+ m_Controls->labelSetWidget->ResetAllTableWidgetItems();
+}
+
+void QmitkSegmentationView::OnShowLabelTable(bool value)
+{
+ m_Controls->labelSetWidget->setVisible(value);
+}
+
+void QmitkSegmentationView::OnGoToLabel(const mitk::Point3D& pos)
+{
+ if (m_RenderWindowPart)
+ {
+ m_RenderWindowPart->SetSelectedPosition(pos);
+ }
+}
+
+void QmitkSegmentationView::OnLabelSetWidgetReset()
+{
+ this->UpdateInterpolatorWidget();
+}
+
/**********************************************************************/
/* private */
/**********************************************************************/
void QmitkSegmentationView::CreateQtPartControl(QWidget* parent)
{
m_Parent = parent;
- m_Controls = new Ui::QmitkSegmentationControls;
+ m_Controls = new Ui::QmitkSegmentationViewControls;
m_Controls->setupUi(parent);
- m_Controls->patImageSelector->SetDataStorage(GetDataStorage());
- m_Controls->patImageSelector->SetNodePredicate(m_ReferencePredicate);
- m_Controls->patImageSelector->SetInvalidInfo("Select an image");
- m_Controls->patImageSelector->SetPopUpTitel("Select an image");
- m_Controls->patImageSelector->SetPopUpHint("Select an image that should be used to define the geometry and bounds of the segmentation.");
-
- m_Controls->segImageSelector->SetDataStorage(GetDataStorage());
- m_Controls->segImageSelector->SetNodePredicate(m_SegmentationPredicate);
- m_Controls->segImageSelector->SetInvalidInfo("Select a segmentation");
- m_Controls->segImageSelector->SetPopUpTitel("Select a segmentation");
- m_Controls->segImageSelector->SetPopUpHint("Select a segmentation that should be modified. Only segmentation with the same geometry and within the bounds of the reference image are selected.");
-
- connect(m_Controls->patImageSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged,
+ // *------------------------
+ // * SHORTCUTS
+ // *------------------------
+ QShortcut* visibilityShortcut = new QShortcut(QKeySequence("CTRL+H"), parent);
+ connect(visibilityShortcut, &QShortcut::activated, this, &QmitkSegmentationView::OnVisibilityShortcutActivated);
+ QShortcut* labelToggleShortcut = new QShortcut(QKeySequence("CTRL+L"), parent);
+ connect(labelToggleShortcut, &QShortcut::activated, this, &QmitkSegmentationView::OnLabelToggleShortcutActivated);
+
+ // *------------------------
+ // * DATA SELECTION WIDGETS
+ // *------------------------
+ m_Controls->referenceNodeSelector->SetDataStorage(GetDataStorage());
+ m_Controls->referenceNodeSelector->SetNodePredicate(m_ReferencePredicate);
+ m_Controls->referenceNodeSelector->SetInvalidInfo("Select an image");
+ m_Controls->referenceNodeSelector->SetPopUpTitel("Select an image");
+ m_Controls->referenceNodeSelector->SetPopUpHint("Select an image that should be used to define the geometry and bounds of the segmentation.");
+
+ m_Controls->workingNodeSelector->SetDataStorage(GetDataStorage());
+ m_Controls->workingNodeSelector->SetNodePredicate(m_SegmentationPredicate);
+ m_Controls->workingNodeSelector->SetInvalidInfo("Select a segmentation");
+ m_Controls->workingNodeSelector->SetPopUpTitel("Select a segmentation");
+ m_Controls->workingNodeSelector->SetPopUpHint("Select a segmentation that should be modified. Only segmentation with the same geometry and within the bounds of the reference image are selected.");
+
+ connect(m_Controls->referenceNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged,
this, &QmitkSegmentationView::OnReferenceSelectionChanged);
- connect(m_Controls->segImageSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged,
+ connect(m_Controls->workingNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged,
this, &QmitkSegmentationView::OnSegmentationSelectionChanged);
+ // *------------------------
+ // * TOOLMANAGER
+ // *------------------------
m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager();
m_ToolManager->SetDataStorage(*(this->GetDataStorage()));
m_ToolManager->InitializeTools();
- QString segTools2D = tr("Add Subtract Fill Erase Paint Wipe 'Region Growing' 'Live Wire' '2D Fast Marching'");
- QString segTools3D = tr("Threshold 'UL Threshold' Otsu 'Fast Marching 3D' 'Region Growing 3D' Watershed Picking");
+ QString segTools2D = tr("Add Subtract Fill Erase Paint Wipe 'Region Growing' 'Fast Marching' 'Live Wire' ");
+ QString segTools3D = tr("Threshold 'UL Threshold' Otsu 'Region Growing 3D' 'Fast Marching 3D' Watershed Picking");
#ifdef __linux__
segTools3D.append(" nnUNet"); // plugin not enabled for MacOS / Windows
#endif
std::regex extSegTool2DRegEx("SegTool2D$");
std::regex extSegTool3DRegEx("SegTool3D$");
auto tools = m_ToolManager->GetTools();
for (const auto &tool : tools)
{
if (std::regex_search(tool->GetNameOfClass(), extSegTool2DRegEx))
{
segTools2D.append(QString(" '%1'").arg(tool->GetName()));
}
else if (std::regex_search(tool->GetNameOfClass(), extSegTool3DRegEx))
{
segTools3D.append(QString(" '%1'").arg(tool->GetName()));
}
}
- // all part of open source MITK
- m_Controls->m_ManualToolSelectionBox2D->SetToolManager(*m_ToolManager);
- m_Controls->m_ManualToolSelectionBox2D->SetGenerateAccelerators(true);
- m_Controls->m_ManualToolSelectionBox2D->SetToolGUIArea(m_Controls->m_ManualToolGUIContainer2D);
- m_Controls->m_ManualToolSelectionBox2D->SetDisplayedToolGroups(segTools2D.toStdString());
- m_Controls->m_ManualToolSelectionBox2D->SetLayoutColumns(3);
- m_Controls->m_ManualToolSelectionBox2D->SetEnabledMode(
+ // setup 2D tools
+ m_Controls->toolSelectionBox2D->SetToolManager(*m_ToolManager);
+ m_Controls->toolSelectionBox2D->SetGenerateAccelerators(true);
+ m_Controls->toolSelectionBox2D->SetToolGUIArea(m_Controls->toolGUIArea2D);
+ m_Controls->toolSelectionBox2D->SetDisplayedToolGroups(segTools2D.toStdString());
+ m_Controls->toolSelectionBox2D->SetLayoutColumns(3);
+ m_Controls->toolSelectionBox2D->SetEnabledMode(
QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible);
- connect(m_Controls->m_ManualToolSelectionBox2D, &QmitkToolSelectionBox::ToolSelected,
+ connect(m_Controls->toolSelectionBox2D, &QmitkToolSelectionBox::ToolSelected,
this, &QmitkSegmentationView::OnManualTool2DSelected);
- //setup 3D Tools
- m_Controls->m_ManualToolSelectionBox3D->SetToolManager(*m_ToolManager);
- m_Controls->m_ManualToolSelectionBox3D->SetGenerateAccelerators(true);
- m_Controls->m_ManualToolSelectionBox3D->SetToolGUIArea(m_Controls->m_ManualToolGUIContainer3D);
- m_Controls->m_ManualToolSelectionBox3D->SetDisplayedToolGroups(segTools3D.toStdString());
- m_Controls->m_ManualToolSelectionBox3D->SetLayoutColumns(3);
- m_Controls->m_ManualToolSelectionBox3D->SetEnabledMode(
+ // setup 3D Tools
+ m_Controls->toolSelectionBox3D->SetToolManager(*m_ToolManager);
+ m_Controls->toolSelectionBox3D->SetGenerateAccelerators(true);
+ m_Controls->toolSelectionBox3D->SetToolGUIArea(m_Controls->toolGUIArea3D);
+ m_Controls->toolSelectionBox3D->SetDisplayedToolGroups(segTools3D.toStdString());
+ m_Controls->toolSelectionBox3D->SetLayoutColumns(3);
+ m_Controls->toolSelectionBox3D->SetEnabledMode(
QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible);
- // create signal/slot connections
- connect(m_Controls->btnNewSegmentation, &QToolButton::clicked, this, &QmitkSegmentationView::CreateNewSegmentation);
- connect(m_Controls->m_SlicesInterpolator, &QmitkSlicesInterpolator::SignalShowMarkerNodes, this, &QmitkSegmentationView::OnShowMarkerNodes);
+ m_Controls->slicesInterpolator->SetDataStorage(this->GetDataStorage());
+
+ // create general signal / slot connections
+ connect(m_Controls->newSegmentationButton, &QToolButton::clicked, this, &QmitkSegmentationView::OnNewSegmentation);
+ connect(m_Controls->slicesInterpolator, &QmitkSlicesInterpolator::SignalShowMarkerNodes, this, &QmitkSegmentationView::OnShowMarkerNodes);
+
+ connect(m_Controls->layersWidget, &QmitkLayersWidget::LayersChanged, this, &QmitkSegmentationView::OnLayersChanged);
+ connect(m_Controls->labelsWidget, &QmitkLabelsWidget::LabelsChanged, this, &QmitkSegmentationView::OnLabelsChanged);
+ connect(m_Controls->labelsWidget, &QmitkLabelsWidget::ShowLabelTable, this, &QmitkSegmentationView::OnShowLabelTable);
+
+ // *------------------------
+ // * LABELSETWIDGET
+ // *------------------------
+ connect(m_Controls->labelSetWidget, &QmitkLabelSetWidget::goToLabel, this, &QmitkSegmentationView::OnGoToLabel);
+ connect(m_Controls->labelSetWidget, &QmitkLabelSetWidget::LabelSetWidgetReset, this, &QmitkSegmentationView::OnLabelSetWidgetReset);
+
+ m_Controls->labelSetWidget->SetDataStorage(this->GetDataStorage());
+ m_Controls->labelSetWidget->SetOrganColors(mitk::OrganNamesHandling::GetDefaultOrganColorString());
+ m_Controls->labelSetWidget->hide();
auto command = itk::SimpleMemberCommand<QmitkSegmentationView>::New();
command->SetCallbackFunction(this, &QmitkSegmentationView::ValidateSelectionInput);
m_RenderingManagerObserverTag =
mitk::RenderingManager::GetInstance()->AddObserver(mitk::RenderingManagerViewsInitializedEvent(), command);
m_RenderWindowPart = this->GetRenderWindowPart();
if (nullptr != m_RenderWindowPart)
{
this->RenderWindowPartActivated(m_RenderWindowPart);
}
// Make sure the GUI notices if appropriate data is already present on creation.
// Should be done last, if everything else is configured because it triggers the autoselection of data.
- m_Controls->patImageSelector->SetAutoSelectNewNodes(true);
- m_Controls->segImageSelector->SetAutoSelectNewNodes(true);
+ m_Controls->referenceNodeSelector->SetAutoSelectNewNodes(true);
+ m_Controls->workingNodeSelector->SetAutoSelectNewNodes(true);
this->UpdateGUI();
}
void QmitkSegmentationView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart)
{
if (m_RenderWindowPart != renderWindowPart)
{
m_RenderWindowPart = renderWindowPart;
}
- if (m_Parent)
+ if (nullptr != m_Parent)
{
m_Parent->setEnabled(true);
}
+ if (nullptr == m_Controls)
+ {
+ return;
+ }
+
// tell the interpolation about tool manager, data storage and render window part
- if (m_Controls)
+ if (nullptr != m_RenderWindowPart)
{
- m_Controls->m_SlicesInterpolator->SetDataStorage(this->GetDataStorage());
QList<mitk::SliceNavigationController*> controllers;
- controllers.push_back(renderWindowPart->GetQmitkRenderWindow("axial")->GetSliceNavigationController());
- controllers.push_back(renderWindowPart->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController());
- controllers.push_back(renderWindowPart->GetQmitkRenderWindow("coronal")->GetSliceNavigationController());
- m_Controls->m_SlicesInterpolator->Initialize(m_ToolManager, controllers);
+ controllers.push_back(m_RenderWindowPart->GetQmitkRenderWindow("axial")->GetSliceNavigationController());
+ controllers.push_back(m_RenderWindowPart->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController());
+ controllers.push_back(m_RenderWindowPart->GetQmitkRenderWindow("coronal")->GetSliceNavigationController());
+ m_Controls->slicesInterpolator->Initialize(m_ToolManager, controllers);
}
}
void QmitkSegmentationView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/)
{
m_RenderWindowPart = nullptr;
- if (m_Parent)
+ if (nullptr != m_Parent)
{
m_Parent->setEnabled(false);
}
}
void QmitkSegmentationView::OnPreferencesChanged(const berry::IBerryPreferences* prefs)
{
- if (m_Controls != nullptr)
+ if (nullptr != m_Controls)
{
bool slimView = prefs->GetBool("slim view", false);
- m_Controls->m_ManualToolSelectionBox2D->SetShowNames(!slimView);
- m_Controls->m_ManualToolSelectionBox3D->SetShowNames(!slimView);
+ m_Controls->toolSelectionBox2D->SetShowNames(!slimView);
+ m_Controls->toolSelectionBox3D->SetShowNames(!slimView);
}
- m_AutoSelectionEnabled = prefs->GetBool("auto selection", false);
+ m_DrawOutline = prefs->GetBool("draw outline", true);
+ m_SelectionMode = prefs->GetBool("selection mode", false);
this->ApplyDisplayOptions();
}
void QmitkSegmentationView::NodeAdded(const mitk::DataNode* node)
{
if (m_SegmentationPredicate->CheckNode(node))
{
this->ApplyDisplayOptions(const_cast<mitk::DataNode*>(node));
}
}
void QmitkSegmentationView::NodeRemoved(const mitk::DataNode* node)
{
- if (m_SegmentationPredicate->CheckNode(node))
+ if (!m_SegmentationPredicate->CheckNode(node))
{
- // remove all possible contour markers of the segmentation
- mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = this->GetDataStorage()->GetDerivations(
- node, mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true)));
+ return;
+ }
- ctkPluginContext* context = mitk::PluginActivator::getContext();
- ctkServiceReference ppmRef = context->getServiceReference<mitk::PlanePositionManagerService>();
- mitk::PlanePositionManagerService* service = context->getService<mitk::PlanePositionManagerService>(ppmRef);
+ // remove all possible contour markers of the segmentation
+ mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = this->GetDataStorage()->GetDerivations(
+ node, mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true)));
- for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it)
- {
- std::string nodeName = node->GetName();
- unsigned int t = nodeName.find_last_of(" ");
- unsigned int id = atof(nodeName.substr(t + 1).c_str()) - 1;
+ ctkPluginContext* context = mitk::PluginActivator::getContext();
+ ctkServiceReference ppmRef = context->getServiceReference<mitk::PlanePositionManagerService>();
+ mitk::PlanePositionManagerService* service = context->getService<mitk::PlanePositionManagerService>(ppmRef);
+
+ for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it)
+ {
+ std::string nodeName = node->GetName();
+ unsigned int t = nodeName.find_last_of(" ");
+ unsigned int id = atof(nodeName.substr(t + 1).c_str()) - 1;
- service->RemovePlanePosition(id);
+ service->RemovePlanePosition(id);
- this->GetDataStorage()->Remove(it->Value());
- }
+ this->GetDataStorage()->Remove(it->Value());
+ }
- context->ungetService(ppmRef);
- service = nullptr;
+ context->ungetService(ppmRef);
+ service = nullptr;
+
+ mitk::Image* image = dynamic_cast<mitk::Image*>(node->GetData());
+ mitk::SurfaceInterpolationController::GetInstance()->RemoveInterpolationSession(image);
+}
- mitk::Image* image = dynamic_cast<mitk::Image*>(node->GetData());
- mitk::SurfaceInterpolationController::GetInstance()->RemoveInterpolationSession(image);
+void QmitkSegmentationView::OnEstablishLabelSetConnection()
+{
+ if (m_WorkingNode.IsNull())
+ {
+ return;
+ }
+
+ auto workingImage = dynamic_cast<mitk::LabelSetImage*>(m_WorkingNode->GetData());
+ if (nullptr == workingImage)
+ {
+ return;
}
+
+ workingImage->GetActiveLabelSet()->AddLabelEvent += mitk::MessageDelegate<QmitkLabelSetWidget>(
+ m_Controls->labelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems);
+ workingImage->GetActiveLabelSet()->RemoveLabelEvent += mitk::MessageDelegate<QmitkLabelSetWidget>(
+ m_Controls->labelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems);
+ workingImage->GetActiveLabelSet()->ModifyLabelEvent += mitk::MessageDelegate<QmitkLabelSetWidget>(
+ m_Controls->labelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems);
+ workingImage->GetActiveLabelSet()->AllLabelsModifiedEvent += mitk::MessageDelegate<QmitkLabelSetWidget>(
+ m_Controls->labelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems);
+ workingImage->GetActiveLabelSet()->ActiveLabelEvent += mitk::MessageDelegate1<QmitkLabelSetWidget,
+ mitk::Label::PixelType>(m_Controls->labelSetWidget, &QmitkLabelSetWidget::SelectLabelByPixelValue);
+
+ // Removed in T27851 to have a chance to react to AfterChangeLayerEvent. Did it brake something?
+ // workingImage->BeforeChangeLayerEvent += mitk::MessageDelegate<QmitkMultiLabelSegmentationView>(
+ // this, &QmitkMultiLabelSegmentationView::OnLooseLabelSetConnection);
+
+ workingImage->AfterChangeLayerEvent += mitk::MessageDelegate<QmitkSegmentationView>(
+ this, &QmitkSegmentationView::UpdateGUI);
+}
+
+void QmitkSegmentationView::OnLooseLabelSetConnection()
+{
+ if (m_WorkingNode.IsNull())
+ {
+ return;
+ }
+
+ auto workingImage = dynamic_cast<mitk::LabelSetImage*>(m_WorkingNode->GetData());
+ if (nullptr == workingImage)
+ {
+ return;
+ }
+
+ // Reset LabelSetWidget Events
+ workingImage->GetActiveLabelSet()->AddLabelEvent -= mitk::MessageDelegate<QmitkLabelSetWidget>(
+ m_Controls->labelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems);
+ workingImage->GetActiveLabelSet()->RemoveLabelEvent -= mitk::MessageDelegate<QmitkLabelSetWidget>(
+ m_Controls->labelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems);
+ workingImage->GetActiveLabelSet()->ModifyLabelEvent -= mitk::MessageDelegate<QmitkLabelSetWidget>(
+ m_Controls->labelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems);
+ workingImage->GetActiveLabelSet()->AllLabelsModifiedEvent -= mitk::MessageDelegate<QmitkLabelSetWidget>(
+ m_Controls->labelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems);
+ workingImage->GetActiveLabelSet()->ActiveLabelEvent -= mitk::MessageDelegate1<QmitkLabelSetWidget,
+ mitk::Label::PixelType>(m_Controls->labelSetWidget, &QmitkLabelSetWidget::SelectLabelByPixelValue);
+
+ // Removed in T27851 to have a chance to react to AfterChangeLayerEvent. Did it brake something?
+ // workingImage->BeforeChangeLayerEvent -= mitk::MessageDelegate<QmitkMultiLabelSegmentationView>(
+ // this, &QmitkMultiLabelSegmentationView::OnLooseLabelSetConnection);
+
+ workingImage->AfterChangeLayerEvent -= mitk::MessageDelegate<QmitkSegmentationView>(
+ this, &QmitkSegmentationView::UpdateGUI);
}
void QmitkSegmentationView::ApplyDisplayOptions()
{
- if (!m_Parent)
+ if (nullptr == m_Parent)
{
return;
}
- if (!m_Controls)
+ if (nullptr == m_Controls)
{
return; // might happen on initialization (preferences loaded)
}
- mitk::DataNode::Pointer workingData = m_ToolManager->GetWorkingData(0);
mitk::DataStorage::SetOfObjects::ConstPointer allImages = this->GetDataStorage()->GetSubset(m_SegmentationPredicate);
for (mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter)
{
this->ApplyDisplayOptions(*iter);
}
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkSegmentationView::ApplyDisplayOptions(mitk::DataNode* node)
{
if (nullptr == node)
{
return;
}
- auto drawOutline = mitk::BoolProperty::New(GetPreferences()->GetBool("draw outline", true));
auto labelSetImage = dynamic_cast<mitk::LabelSetImage*>(node->GetData());
if (nullptr != labelSetImage)
{
- // node is actually a multi label segmentation,
- // but its outline property can be set in the 'single label' segmentation preference page as well
- node->SetProperty("labelset.contour.active", drawOutline);
+ // node is a multi label segmentation
+ // its outline property can be set in the segmentation preference page
+ node->SetProperty("labelset.contour.active", mitk::BoolProperty::New(m_DrawOutline));
+
// force render window update to show outline
node->GetData()->Modified();
}
- else
+ else if (nullptr != node->GetData())
{
- // node is a 'single label' segmentation
+ // node is a legacy binary segmentation
bool isBinary = false;
node->GetBoolProperty("binary", isBinary);
if (isBinary)
{
- node->SetProperty("outline binary", drawOutline);
+ node->SetProperty("outline binary", mitk::BoolProperty::New(m_DrawOutline));
node->SetProperty("outline width", mitk::FloatProperty::New(2.0));
// force render window update to show outline
node->GetData()->Modified();
}
}
}
void QmitkSegmentationView::OnContourMarkerSelected(const mitk::DataNode* node)
{
QmitkRenderWindow* selectedRenderWindow = nullptr;
auto* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN);
auto* axialRenderWindow = renderWindowPart->GetQmitkRenderWindow("axial");
auto* sagittalRenderWindow = renderWindowPart->GetQmitkRenderWindow("sagittal");
auto* coronalRenderWindow = renderWindowPart->GetQmitkRenderWindow("coronal");
- auto* _3DRenderWindow = renderWindowPart->GetQmitkRenderWindow("3d");
+ auto* threeDRenderWindow = renderWindowPart->GetQmitkRenderWindow("3d");
bool PlanarFigureInitializedWindow = false;
// find initialized renderwindow
if (node->GetBoolProperty("PlanarFigureInitializedWindow",
PlanarFigureInitializedWindow, axialRenderWindow->GetRenderer()))
{
selectedRenderWindow = axialRenderWindow;
}
if (!selectedRenderWindow && node->GetBoolProperty(
"PlanarFigureInitializedWindow", PlanarFigureInitializedWindow,
sagittalRenderWindow->GetRenderer()))
{
selectedRenderWindow = sagittalRenderWindow;
}
if (!selectedRenderWindow && node->GetBoolProperty(
"PlanarFigureInitializedWindow", PlanarFigureInitializedWindow,
coronalRenderWindow->GetRenderer()))
{
selectedRenderWindow = coronalRenderWindow;
}
if (!selectedRenderWindow && node->GetBoolProperty(
"PlanarFigureInitializedWindow", PlanarFigureInitializedWindow,
- _3DRenderWindow->GetRenderer()))
+ threeDRenderWindow->GetRenderer()))
{
- selectedRenderWindow = _3DRenderWindow;
+ selectedRenderWindow = threeDRenderWindow;
}
// make node visible
- if (selectedRenderWindow)
+ if (nullptr != selectedRenderWindow)
{
std::string nodeName = node->GetName();
unsigned int t = nodeName.find_last_of(" ");
unsigned int id = atof(nodeName.substr(t + 1).c_str()) - 1;
- {
- ctkPluginContext* context = mitk::PluginActivator::getContext();
- ctkServiceReference ppmRef = context->getServiceReference<mitk::PlanePositionManagerService>();
- mitk::PlanePositionManagerService* service = context->getService<mitk::PlanePositionManagerService>(ppmRef);
- selectedRenderWindow->GetSliceNavigationController()->ExecuteOperation(service->GetPlanePosition(id));
- context->ungetService(ppmRef);
- }
+ ctkPluginContext* context = mitk::PluginActivator::getContext();
+ ctkServiceReference ppmRef = context->getServiceReference<mitk::PlanePositionManagerService>();
+ mitk::PlanePositionManagerService* service = context->getService<mitk::PlanePositionManagerService>(ppmRef);
+ selectedRenderWindow->GetSliceNavigationController()->ExecuteOperation(service->GetPlanePosition(id));
+ context->ungetService(ppmRef);
selectedRenderWindow->GetRenderer()->GetCameraController()->Fit();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkSegmentationView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList<mitk::DataNode::Pointer>& nodes)
{
- if (nodes.size() != 0)
+ if (0 == nodes.size())
{
- std::string markerName = "Position";
- unsigned int numberOfNodes = nodes.size();
- std::string nodeName = nodes.at(0)->GetName();
- if ((numberOfNodes == 1) && (nodeName.find(markerName) == 0))
- {
- this->OnContourMarkerSelected(nodes.at(0));
- return;
- }
+ return;
+ }
+
+ std::string markerName = "Position";
+ unsigned int numberOfNodes = nodes.size();
+ std::string nodeName = nodes.at(0)->GetName();
+ if ((numberOfNodes == 1) && (nodeName.find(markerName) == 0))
+ {
+ this->OnContourMarkerSelected(nodes.at(0));
+ return;
}
}
void QmitkSegmentationView::ResetMouseCursor()
{
if (m_MouseCursorSet)
{
mitk::ApplicationCursor::GetInstance()->PopCursor();
m_MouseCursorSet = false;
}
}
void QmitkSegmentationView::SetMouseCursor(const us::ModuleResource& resource, int hotspotX, int hotspotY)
{
// Remove previously set mouse cursor
if (m_MouseCursorSet)
+ {
this->ResetMouseCursor();
+ }
if (resource)
{
us::ModuleResourceStream cursor(resource, std::ios::binary);
mitk::ApplicationCursor::GetInstance()->PushCursor(cursor, hotspotX, hotspotY);
m_MouseCursorSet = true;
}
}
void QmitkSegmentationView::UpdateGUI()
{
mitk::DataNode* referenceNode = m_ToolManager->GetReferenceData(0);
bool hasReferenceNode = referenceNode != nullptr;
mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0);
bool hasWorkingNode = workingNode != nullptr;
- m_Controls->btnNewSegmentation->setEnabled(false);
- m_Controls->m_SlicesInterpolator->setEnabled(false);
+ m_Controls->newSegmentationButton->setEnabled(false);
if (hasReferenceNode)
{
- m_Controls->btnNewSegmentation->setEnabled(true);
+ m_Controls->newSegmentationButton->setEnabled(true);
}
if (hasWorkingNode && hasReferenceNode)
{
- m_Controls->m_SlicesInterpolator->setEnabled(true);
-
int layer = -1;
referenceNode->GetIntProperty("layer", layer);
workingNode->SetIntProperty("layer", layer + 1);
}
+ this->UpdateInterpolatorWidget();
+ m_Controls->layersWidget->UpdateGUI();
+ m_Controls->labelsWidget->UpdateGUI();
+
this->ValidateSelectionInput();
}
+void QmitkSegmentationView::UpdateInterpolatorWidget()
+{
+ m_Controls->slicesInterpolator->setEnabled(false);
+
+ if (m_WorkingNode.IsNull())
+ {
+ return;
+ }
+
+ auto labelSetImage = dynamic_cast<mitk::LabelSetImage*>(m_WorkingNode->GetData());
+ if (nullptr == labelSetImage)
+ {
+ return;
+ }
+
+ int numberOfLabels = labelSetImage->GetNumberOfLabels(labelSetImage->GetActiveLayer());
+ if (2 == numberOfLabels) // fix for T27319: exterior is label 0, first label is label 1
+ {
+ m_Controls->interpolatorWarningLabel->hide();
+ m_Controls->slicesInterpolator->setEnabled(true);
+ }
+ else
+ {
+ m_Controls->interpolatorWarningLabel->show();
+ m_Controls->interpolatorWarningLabel->setText("<font color=\"red\">Interpolation only works for single label segmentations.</font>");
+ }
+}
+
void QmitkSegmentationView::ValidateSelectionInput()
{
this->UpdateWarningLabel("");
+ m_Controls->labelSetWidget->setEnabled(false);
+
// the argument is actually not used
// enable status depends on the tool manager selection
- m_Controls->m_ManualToolSelectionBox2D->setEnabled(false);
- m_Controls->m_ManualToolSelectionBox3D->setEnabled(false);
+ m_Controls->toolSelectionBox2D->setEnabled(false);
+ m_Controls->toolSelectionBox3D->setEnabled(false);
- mitk::DataNode* referenceNode = m_Controls->patImageSelector->GetSelectedNode();
- mitk::DataNode* workingNode = m_Controls->segImageSelector->GetSelectedNode();
+ mitk::DataNode* referenceNode = m_Controls->referenceNodeSelector->GetSelectedNode();
+ mitk::DataNode* workingNode = m_Controls->workingNodeSelector->GetSelectedNode();
if (nullptr == referenceNode)
{
return;
}
if (nullptr == workingNode)
{
return;
}
mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart();
auto workingNodeIsVisible = renderWindowPart &&
workingNode->IsVisible(renderWindowPart->GetQmitkRenderWindow("axial")->GetRenderer());
if (!workingNodeIsVisible)
{
this->UpdateWarningLabel(tr("The selected segmentation is currently not visible!"));
return;
}
/*
* Here we check whether the geometry of the selected segmentation image is aligned with the worldgeometry.
* At the moment it is not supported to use a geometry different from the selected image for reslicing.
* For further information see Bug 16063
*/
- const mitk::BaseGeometry *workingNodeGeo = workingNode->GetData()->GetGeometry();
- const mitk::BaseGeometry *worldGeo =
+ const mitk::BaseGeometry* workingNodeGeo = workingNode->GetData()->GetGeometry();
+ const mitk::BaseGeometry* worldGeo =
renderWindowPart->GetQmitkRenderWindow("3d")->GetSliceNavigationController()->GetCurrentGeometry3D();
if (nullptr != workingNodeGeo && nullptr != worldGeo)
{
if (mitk::Equal(*workingNodeGeo->GetBoundingBox(), *worldGeo->GetBoundingBox(), mitk::eps, true))
{
m_ToolManager->SetReferenceData(referenceNode);
m_ToolManager->SetWorkingData(workingNode);
- m_Controls->m_ManualToolSelectionBox2D->setEnabled(true);
- m_Controls->m_ManualToolSelectionBox3D->setEnabled(true);
+ m_Controls->labelSetWidget->setEnabled(true);
+ m_Controls->toolSelectionBox2D->setEnabled(true);
+ m_Controls->toolSelectionBox3D->setEnabled(true);
return;
}
}
m_ToolManager->SetReferenceData(referenceNode);
m_ToolManager->SetWorkingData(nullptr);
this->UpdateWarningLabel(tr("Please perform a reinit on the segmentation image!"));
}
void QmitkSegmentationView::UpdateWarningLabel(QString text)
{
if (text.size() == 0)
{
- m_Controls->lblSegmentationWarnings->hide();
+ m_Controls->selectionWarningLabel->hide();
}
else
{
- m_Controls->lblSegmentationWarnings->show();
+ m_Controls->selectionWarningLabel->show();
+ m_Controls->selectionWarningLabel->setText("<font color=\"red\">" + text + "</font>");
}
- m_Controls->lblSegmentationWarnings->setText("<font color=\"red\">" + text + "</font>");
}
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h
index 7b3187260b..d202af58f5 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h
@@ -1,122 +1,153 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef QMITKSEGMENTATIONVIEW_H
#define QMITKSEGMENTATIONVIEW_H
-#include "ui_QmitkSegmentationControls.h"
+#include "ui_QmitkSegmentationViewControls.h"
#include <QmitkAbstractView.h>
#include <mitkIRenderWindowPartListener.h>
#include <berryIBerryPreferences.h>
/**
* @brief The segmentation view provides a set of tool to use different segmentation algorithms.
* It provides two selection widgets to load an image node and a segmentation node
* on which to perform the segmentation. Creating new segmentation nodes is also possible.
* The available segmentation tools are grouped into "2D"- and "3D"-tools.
*
* Most segmentation tools / algorithms need some kind of user interaction, where the
* user is asked to draw something in the image display or set some seed points / start values.
* The tools also often provide additional propeties so that a user can modify the
* algorithm's behavior.
*
+* This class additionally provides options to work with different layers (create new layers,
+* switch between layers).
+* Moreover, a multilabel widget displays all the existing labels of a multilabel segmentation
+* for the currently active layer.
+* The multilabel widget allows to control the labels by creatin new one, removing existing ones,
+* showing / hiding single labels, merging labels, (re-)naming them etc.
+*
* Additionally the view provides an option to create "2D"- and "3D"-interpolations between
* neighboring segmentation masks on unsegmented slices.
+* Interpolation for multilabel segmentations is currently not implemented.
*/
class QmitkSegmentationView : public QmitkAbstractView, public mitk::IRenderWindowPartListener
{
Q_OBJECT
public:
static const std::string VIEW_ID;
QmitkSegmentationView();
~QmitkSegmentationView() override;
private Q_SLOTS:
// reaction to the selection of a new reference image in the selection widget
void OnReferenceSelectionChanged(QList<mitk::DataNode::Pointer> nodes);
// reaction to the selection of a new segmentation image in the selection widget
void OnSegmentationSelectionChanged(QList<mitk::DataNode::Pointer> nodes);
+ // reaction to the shortcut ("CTRL+H") for toggling the visibility of the working node
+ void OnVisibilityShortcutActivated();
+
+ // reaction to the shortcut ("CTRL+L") for iterating over all labels
+ void OnLabelToggleShortcutActivated();
+
// reaction to the button "New segmentation"
- void CreateNewSegmentation();
+ void OnNewSegmentation();
void OnManualTool2DSelected(int id);
void OnShowMarkerNodes(bool);
+ void OnLayersChanged();
+
+ void OnLabelsChanged();
+
+ void OnShowLabelTable(bool);
+
+ void OnGoToLabel(const mitk::Point3D &pos);
+
+ void OnLabelSetWidgetReset();
+
private:
void CreateQtPartControl(QWidget* parent) override;
void SetFocus() override {}
void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) override;
void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) override;
void OnPreferencesChanged(const berry::IBerryPreferences* prefs) override;
- void NodeAdded(const mitk::DataNode *node) override;
+ void NodeAdded(const mitk::DataNode* node) override;
void NodeRemoved(const mitk::DataNode* node) override;
+ void OnEstablishLabelSetConnection();
+
+ void OnLooseLabelSetConnection();
+
// make sure all images / segmentations look according to the user preference settings
void ApplyDisplayOptions();
// decorates a DataNode according to the user preference settings
void ApplyDisplayOptions(mitk::DataNode* node);
// If a contourmarker is selected, the plane in the related widget will be reoriented according to the marker`s geometry
void OnContourMarkerSelected(const mitk::DataNode* node);
void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList<mitk::DataNode::Pointer> &nodes) override;
void ResetMouseCursor();
void SetMouseCursor(const us::ModuleResource&, int hotspotX, int hotspotY);
void UpdateGUI();
+ void UpdateInterpolatorWidget();
+
void ValidateSelectionInput();
void UpdateWarningLabel(QString text);
QWidget* m_Parent;
- Ui::QmitkSegmentationControls* m_Controls;
+ Ui::QmitkSegmentationViewControls* m_Controls;
mitk::IRenderWindowPart* m_RenderWindowPart;
mitk::ToolManager* m_ToolManager;
mitk::DataNode::Pointer m_ReferenceNode;
mitk::DataNode::Pointer m_WorkingNode;
typedef std::map<mitk::DataNode*, unsigned long> NodeTagMapType;
- NodeTagMapType m_WorkingDataObserverTags;
+ NodeTagMapType m_WorkingDataObserverTags;
unsigned int m_RenderingManagerObserverTag;
mitk::NodePredicateAnd::Pointer m_ReferencePredicate;
mitk::NodePredicateAnd::Pointer m_SegmentationPredicate;
- bool m_AutoSelectionEnabled;
+ bool m_DrawOutline;
+ bool m_SelectionMode;
bool m_MouseCursorSet;
};
#endif // QMITKSEGMENTATIONVIEW_H
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationControls.ui b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationViewControls.ui
similarity index 50%
rename from Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationControls.ui
rename to Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationViewControls.ui
index cbe3ef5e96..958d8992b7 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationControls.ui
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationViewControls.ui
@@ -1,289 +1,344 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
- <class>QmitkSegmentationControls</class>
- <widget class="QWidget" name="QmitkSegmentationControls">
+ <class>QmitkSegmentationViewControls</class>
+ <widget class="QWidget" name="QmitkSegmentationViewControls">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>237</width>
- <height>591</height>
+ <width>300</width>
+ <height>600</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="windowTitle">
- <string>QmitkSegmentation</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_6">
+ <layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
- <string>Data Selection</string>
+ <string>Data selection</string>
</property>
<layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0">
- <widget class="QLabel" name="lblPatientImage">
- <property name="text">
- <string>Selected Image</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QmitkSingleNodeSelectionWidget" name="patImageSelector" native="true">
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>40</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="lblSegmentation">
- <property name="text">
- <string>Segmentation</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QmitkSingleNodeSelectionWidget" name="segImageSelector" native="true">
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>40</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="1" column="2">
- <widget class="QToolButton" name="btnNewSegmentation">
- <property name="toolTip">
- <string>Create a new segmentation</string>
- </property>
- <property name="text">
- <string>&amp;New...</string>
- </property>
- <property name="toolButtonStyle">
- <enum>Qt::ToolButtonTextOnly</enum>
- </property>
- </widget>
- </item>
- <item row="2" column="0" colspan="3">
- <widget class="QLabel" name="lblSegmentationWarnings">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
+ <item row="0" column="0">
+ <widget class="QLabel" name="referenceNodeLabel">
+ <property name="text">
+ <string>Selected image</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QmitkSingleNodeSelectionWidget" name="referenceNodeSelector" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>40</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="workingNodeLabel">
+ <property name="text">
+ <string>Selected segmentation</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QmitkSingleNodeSelectionWidget" name="workingNodeSelector" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>40</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QToolButton" name="newSegmentationButton">
+ <property name="toolTip">
+ <string>Create a new segmentation</string>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset>
+ <normaloff>:/Qmitk/NewSegmentation_48x48.png</normaloff>:/Qmitk/NewSegmentation_48x48.png</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>28</width>
+ <height>28</height>
+ </size>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="3">
+ <widget class="QLabel" name="selectionWarningLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QmitkLayersWidget" name="layersWidget" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QmitkLabelsWidget" name="labelsWidget" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QmitkLabelSetWidget" name="labelSetWidget" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
</widget>
</item>
<item>
<widget class="QTabWidget" name="tabWidgetSegmentationTools">
<property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">QTabWidget::tab-bar { alignment: middle; }</string>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab2DTools">
<attribute name="title">
- <string>2D Tools</string>
+ <string>2D tools</string>
</attribute>
- <layout class="QVBoxLayout" name="verticalLayout">
+ <layout class="QVBoxLayout" name="verticalLayout2D">
<item>
- <widget class="QmitkToolGUIArea" name="m_ManualToolGUIContainer2D" native="true">
+ <widget class="QmitkToolGUIArea" name="toolGUIArea2D" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
</widget>
</item>
<item>
- <widget class="QmitkToolSelectionBox" name="m_ManualToolSelectionBox2D" native="true">
+ <widget class="QmitkToolSelectionBox" name="toolSelectionBox2D" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
</widget>
</item>
<item>
- <widget class="QmitkSlicesInterpolator" name="m_SlicesInterpolator" native="true">
+ <widget class="QLabel" name="interpolatorWarningLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QmitkSlicesInterpolator" name="slicesInterpolator" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
</widget>
</item>
<item>
- <spacer name="verticalSpacer_1">
+ <spacer name="verticalSpacer2D">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab3DTools">
<attribute name="title">
- <string>3D Tools</string>
+ <string>3D tools</string>
</attribute>
- <layout class="QVBoxLayout" name="verticalLayout_2">
+ <layout class="QVBoxLayout" name="verticalLayout3D">
<item>
- <widget class="QmitkToolGUIArea" name="m_ManualToolGUIContainer3D" native="true">
+ <widget class="QmitkToolGUIArea" name="toolGUIArea3D" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
</widget>
</item>
<item>
- <widget class="QmitkToolSelectionBox" name="m_ManualToolSelectionBox3D" native="true">
+ <widget class="QmitkToolSelectionBox" name="toolSelectionBox3D" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
</widget>
</item>
<item>
- <spacer name="verticalSpacer_2">
+ <spacer name="verticalSpacer3D">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
- <layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>QmitkSingleNodeSelectionWidget</class>
<extends>QWidget</extends>
<header location="global">QmitkSingleNodeSelectionWidget.h</header>
<container>1</container>
</customwidget>
+ <customwidget>
+ <class>QmitkLayersWidget</class>
+ <extends>QWidget</extends>
+ <header location="global">internal/Common/QmitkLayersWidget.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>QmitkLabelsWidget</class>
+ <extends>QWidget</extends>
+ <header location="global">internal/Common/QmitkLabelsWidget.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>QmitkLabelSetWidget</class>
+ <extends>QWidget</extends>
+ <header location="global">QmitkLabelSetWidget.h</header>
+ <container>1</container>
+ </customwidget>
<customwidget>
<class>QmitkToolSelectionBox</class>
<extends>QWidget</extends>
<header location="global">QmitkToolSelectionBox.h</header>
</customwidget>
<customwidget>
<class>QmitkToolGUIArea</class>
<extends>QWidget</extends>
<header location="global">QmitkToolGUIArea.h</header>
</customwidget>
<customwidget>
<class>QmitkSlicesInterpolator</class>
<extends>QWidget</extends>
<header location="global">QmitkSlicesInterpolator.h</header>
</customwidget>
</customwidgets>
- <includes>
- <include location="global">QmitkToolGUIArea.h</include>
- <include location="global">QmitkToolSelectionBox.h</include>
- <include location="global">QmitkSlicesInterpolator.h</include>
- </includes>
<resources>
<include location="../../resources/segmentation.qrc"/>
- <include location="../../resources/segmentation.qrc"/>
</resources>
<connections/>
</ui>
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp
index 86a5468a8c..b318653e4a 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp
@@ -1,382 +1,382 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "QmitkImageMaskingWidget.h"
#include "mitkImage.h"
#include "../../Common/QmitkDataSelectionWidget.h"
#include <mitkException.h>
#include <mitkExceptionMacro.h>
#include <mitkImageStatisticsHolder.h>
#include <mitkMaskImageFilter.h>
#include <mitkProgressBar.h>
#include <mitkSliceNavigationController.h>
#include <mitkSurfaceToImageFilter.h>
#include <mitkImageAccessByItk.h>
#include <mitkNodePredicateAnd.h>
#include <mitkNodePredicateDimension.h>
#include <mitkNodePredicateGeometry.h>
#include <mitkNodePredicateNot.h>
#include <QMessageBox>
#include <limits>
namespace
{
bool IsSurface(const mitk::DataNode* dataNode)
{
if (nullptr != dataNode)
{
if (nullptr != dynamic_cast<const mitk::Surface*>(dataNode->GetData()))
return true;
}
return false;
}
}
static const char* const HelpText = "Select an image and a segmentation or surface";
QmitkImageMaskingWidget::QmitkImageMaskingWidget(mitk::SliceNavigationController* timeNavigationController, QWidget* parent)
: QmitkSegmentationUtilityWidget(timeNavigationController, parent)
{
m_Controls.setupUi(this);
m_Controls.dataSelectionWidget->AddDataSelection(QmitkDataSelectionWidget::ImagePredicate);
m_Controls.dataSelectionWidget->AddDataSelection(QmitkDataSelectionWidget::SegmentationOrSurfacePredicate);
m_Controls.dataSelectionWidget->SetHelpText(HelpText);
// T28795: Disable 2-d reference images since they do not work yet (segmentations are at least 3-d images with a single slice)
m_Controls.dataSelectionWidget->SetPredicate(0, mitk::NodePredicateAnd::New(
mitk::NodePredicateNot::New(mitk::NodePredicateDimension::New(2)),
m_Controls.dataSelectionWidget->GetPredicate(0)));
this->EnableButtons(false);
connect(m_Controls.btnMaskImage, SIGNAL(clicked()), this, SLOT(OnMaskImagePressed()));
connect(m_Controls.rbnCustom, SIGNAL(toggled(bool)), this, SLOT(OnCustomValueButtonToggled(bool)));
connect(m_Controls.dataSelectionWidget, SIGNAL(SelectionChanged(unsigned int, const mitk::DataNode*)),
this, SLOT(OnSelectionChanged(unsigned int, const mitk::DataNode*)));
if( m_Controls.dataSelectionWidget->GetSelection(0).IsNotNull() &&
m_Controls.dataSelectionWidget->GetSelection(1).IsNotNull() )
{
this->OnSelectionChanged(0, m_Controls.dataSelectionWidget->GetSelection(0));
}
}
QmitkImageMaskingWidget::~QmitkImageMaskingWidget()
{
}
void QmitkImageMaskingWidget::OnSelectionChanged(unsigned int index, const mitk::DataNode *selection)
{
auto *dataSelectionWidget = m_Controls.dataSelectionWidget;
auto node0 = dataSelectionWidget->GetSelection(0);
if (index == 0)
{
dataSelectionWidget->SetPredicate(1, QmitkDataSelectionWidget::SegmentationOrSurfacePredicate);
if (node0.IsNotNull())
{
dataSelectionWidget->SetPredicate(1, mitk::NodePredicateAnd::New(
mitk::NodePredicateGeometry::New(node0->GetData()->GetGeometry()),
dataSelectionWidget->GetPredicate(1)));
}
}
auto node1 = dataSelectionWidget->GetSelection(1);
if (node0.IsNull() || node1.IsNull())
{
dataSelectionWidget->SetHelpText(HelpText);
this->EnableButtons(false);
}
else
{
this->SelectionControl(index, selection);
}
}
void QmitkImageMaskingWidget::SelectionControl(unsigned int index, const mitk::DataNode* selection)
{
QmitkDataSelectionWidget* dataSelectionWidget = m_Controls.dataSelectionWidget;
mitk::DataNode::Pointer node = dataSelectionWidget->GetSelection(index);
//if Image-Masking is enabled, check if image-dimension of reference and binary image is identical
if( !IsSurface(dataSelectionWidget->GetSelection(1)) )
{
if( dataSelectionWidget->GetSelection(0) == dataSelectionWidget->GetSelection(1) )
{
dataSelectionWidget->SetHelpText("Select two different images above");
this->EnableButtons(false);
return;
}
else if( node.IsNotNull() && selection )
{
mitk::Image::Pointer referenceImage = dynamic_cast<mitk::Image*> ( dataSelectionWidget->GetSelection(0)->GetData() );
mitk::Image::Pointer maskImage = dynamic_cast<mitk::Image*> ( dataSelectionWidget->GetSelection(1)->GetData() );
if (maskImage.IsNull())
{
dataSelectionWidget->SetHelpText("Different image sizes cannot be masked");
this->EnableButtons(false);
return;
}
}
else
{
dataSelectionWidget->SetHelpText(HelpText);
return;
}
}
dataSelectionWidget->SetHelpText("");
this->EnableButtons();
}
void QmitkImageMaskingWidget::EnableButtons(bool enable)
{
m_Controls.grpBackgroundValue->setEnabled(enable);
m_Controls.btnMaskImage->setEnabled(enable);
}
template<typename TPixel, unsigned int VImageDimension>
void GetRange(const itk::Image<TPixel, VImageDimension>*, double& bottom, double& top)
{
bottom = std::numeric_limits<TPixel>::lowest();
top = std::numeric_limits<TPixel>::max();
}
void QmitkImageMaskingWidget::OnCustomValueButtonToggled(bool checked)
{
m_Controls.txtCustom->setEnabled(checked);
}
void QmitkImageMaskingWidget::OnMaskImagePressed()
{
//Disable Buttons during calculation and initialize Progressbar
this->EnableButtons(false);
mitk::ProgressBar::GetInstance()->AddStepsToDo(4);
mitk::ProgressBar::GetInstance()->Progress();
QmitkDataSelectionWidget* dataSelectionWidget = m_Controls.dataSelectionWidget;
//create result image, get mask node and reference image
mitk::Image::Pointer resultImage(nullptr);
mitk::DataNode::Pointer maskingNode = dataSelectionWidget->GetSelection(1);
mitk::Image::Pointer referenceImage = static_cast<mitk::Image*>(dataSelectionWidget->GetSelection(0)->GetData());
if(referenceImage.IsNull() || maskingNode.IsNull() )
{
MITK_ERROR << "Selection does not contain an image";
QMessageBox::information( this, "Image and Surface Masking", "Selection does not contain an image", QMessageBox::Ok );
m_Controls.btnMaskImage->setEnabled(true);
return;
}
//Do Image-Masking
if (!IsSurface(maskingNode))
{
mitk::ProgressBar::GetInstance()->Progress();
mitk::Image::Pointer maskImage = dynamic_cast<mitk::Image*> ( maskingNode->GetData() );
if(maskImage.IsNull() )
{
MITK_ERROR << "Selection does not contain a segmentation";
QMessageBox::information( this, "Image and Surface Masking", "Selection does not contain a segmentation", QMessageBox::Ok );
this->EnableButtons();
return;
}
resultImage = this->MaskImage(referenceImage, maskImage);
}
//Do Surface-Masking
else
{
mitk::ProgressBar::GetInstance()->Progress();
//1. convert surface to image
mitk::Surface::Pointer surface = dynamic_cast<mitk::Surface*> ( maskingNode->GetData() );
//TODO Get 3D Surface of current time step
if(surface.IsNull())
{
MITK_ERROR << "Selection does not contain a surface";
QMessageBox::information( this, "Image and Surface Masking", "Selection does not contain a surface", QMessageBox::Ok );
this->EnableButtons();
return;
}
mitk::Image::Pointer maskImage = this->ConvertSurfaceToImage( referenceImage, surface );
//2. mask reference image with mask image
if(maskImage.IsNotNull() &&
referenceImage->GetLargestPossibleRegion().GetSize() == maskImage->GetLargestPossibleRegion().GetSize() )
{
resultImage = this->MaskImage( referenceImage, maskImage );
}
}
mitk::ProgressBar::GetInstance()->Progress();
if( resultImage.IsNull() )
{
MITK_ERROR << "Masking failed";
QMessageBox::information( this, "Image and Surface Masking", "Masking failed. For more information please see logging window.", QMessageBox::Ok );
this->EnableButtons();
mitk::ProgressBar::GetInstance()->Progress(4);
return;
}
//Add result to data storage
this->AddToDataStorage(
dataSelectionWidget->GetDataStorage(),
resultImage,
dataSelectionWidget->GetSelection(0)->GetName() + "_" + dataSelectionWidget->GetSelection(1)->GetName(),
dataSelectionWidget->GetSelection(0));
this->EnableButtons();
mitk::ProgressBar::GetInstance()->Progress();
}
mitk::Image::Pointer QmitkImageMaskingWidget::MaskImage(mitk::Image::Pointer referenceImage, mitk::Image::Pointer maskImage )
{
mitk::ScalarType backgroundValue = 0.0;
if (m_Controls.rbnMinimum->isChecked())
{
backgroundValue = referenceImage->GetStatistics()->GetScalarValueMin();
}
else if (m_Controls.rbnCustom->isChecked())
{
auto warningTitle = QStringLiteral("Invalid custom pixel value");
bool ok = false;
auto originalBackgroundValue = m_Controls.txtCustom->text().toDouble(&ok);
if (!ok)
{
// Input is not even a number
QMessageBox::warning(nullptr, warningTitle, "Please enter a valid number as custom pixel value.");
return nullptr;
}
else
{
// Clamp to the numerical limits of the pixel/component type
double bottom, top;
if (referenceImage->GetDimension() == 4)
{
AccessFixedDimensionByItk_n(referenceImage, GetRange, 4, (bottom, top));
}
else
{
AccessByItk_n(referenceImage, GetRange, (bottom, top));
}
backgroundValue = std::max(bottom, std::min(originalBackgroundValue, top));
// Get rid of decimals for integral numbers
auto type = referenceImage->GetPixelType().GetComponentType();
- if (type != itk::ImageIOBase::FLOAT && type != itk::ImageIOBase::DOUBLE)
+ if (type != itk::IOComponentEnum::FLOAT && type != itk::IOComponentEnum::DOUBLE)
backgroundValue = std::round(backgroundValue);
}
// Ask the user for permission before correcting their input
if (std::abs(originalBackgroundValue - backgroundValue) > 1e-4)
{
auto warningText = QString(
"<p>The custom pixel value <b>%1</b> lies not within the range of valid pixel values for the selected image.</p>"
"<p>Apply the closest valid pixel value <b>%2</b> instead?</p>").arg(originalBackgroundValue).arg(backgroundValue);
auto ret = QMessageBox::warning(
nullptr,
warningTitle,
warningText,
QMessageBox::StandardButton::Apply | QMessageBox::StandardButton::Cancel,
QMessageBox::StandardButton::Apply);
if (QMessageBox::StandardButton::Apply != ret)
return nullptr;
m_Controls.txtCustom->setText(QString("%1").arg(backgroundValue));
}
}
auto maskFilter = mitk::MaskImageFilter::New();
maskFilter->SetInput(referenceImage);
maskFilter->SetMask(maskImage);
maskFilter->OverrideOutsideValueOn();
maskFilter->SetOutsideValue(backgroundValue);
try
{
maskFilter->Update();
}
catch(const itk::ExceptionObject& e)
{
MITK_ERROR << e.GetDescription();
return nullptr;
}
return maskFilter->GetOutput();
}
mitk::Image::Pointer QmitkImageMaskingWidget::ConvertSurfaceToImage( mitk::Image::Pointer image, mitk::Surface::Pointer surface )
{
mitk::ProgressBar::GetInstance()->AddStepsToDo(2);
mitk::ProgressBar::GetInstance()->Progress();
mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New();
surfaceToImageFilter->MakeOutputBinaryOn();
surfaceToImageFilter->SetInput(surface);
surfaceToImageFilter->SetImage(image);
try
{
surfaceToImageFilter->Update();
}
catch(itk::ExceptionObject& excpt)
{
MITK_ERROR << excpt.GetDescription();
return nullptr;
}
mitk::ProgressBar::GetInstance()->Progress();
mitk::Image::Pointer resultImage = mitk::Image::New();
resultImage = surfaceToImageFilter->GetOutput();
return resultImage;
}
void QmitkImageMaskingWidget::AddToDataStorage(mitk::DataStorage::Pointer dataStorage, mitk::Image::Pointer segmentation, const std::string& name, mitk::DataNode::Pointer parent )
{
auto dataNode = mitk::DataNode::New();
dataNode->SetName(name);
dataNode->SetData(segmentation);
if (parent.IsNotNull())
{
mitk::LevelWindow levelWindow;
parent->GetLevelWindow(levelWindow);
dataNode->SetLevelWindow(levelWindow);
}
dataStorage->Add(dataNode, parent);
}
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/mitkPluginActivator.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/mitkPluginActivator.cpp
index 6d27f32fde..2e080b5c7e 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/mitkPluginActivator.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/mitkPluginActivator.cpp
@@ -1,61 +1,66 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkPluginActivator.h"
#include "QmitkSegmentationView.h"
-#include "QmitkCreatePolygonModelAction.h"
-#include "QmitkAutocropAction.h"
-#include "QmitkAutocropLabelSetImageAction.h"
#include "QmitkSegmentationPreferencePage.h"
#include "SegmentationUtilities/QmitkSegmentationUtilitiesView.h"
+#include "QmitkAutocropAction.h"
+#include "QmitkAutocropLabelSetImageAction.h"
+#include "QmitkCreatePolygonModelAction.h"
+#include "QmitkLoadMultiLabelPresetAction.h"
+#include "QmitkSaveMultiLabelPresetAction.h"
+
using namespace mitk;
ctkPluginContext* PluginActivator::m_context = nullptr;
PluginActivator* PluginActivator::m_Instance = nullptr;
PluginActivator::PluginActivator()
{
m_Instance = this;
}
PluginActivator::~PluginActivator()
{
m_Instance = nullptr;
}
void PluginActivator::start(ctkPluginContext *context)
{
BERRY_REGISTER_EXTENSION_CLASS(QmitkSegmentationView, context)
- BERRY_REGISTER_EXTENSION_CLASS(QmitkCreatePolygonModelAction, context)
- BERRY_REGISTER_EXTENSION_CLASS(QmitkAutocropAction, context)
- BERRY_REGISTER_EXTENSION_CLASS(QmitkAutocropLabelSetImageAction, context)
BERRY_REGISTER_EXTENSION_CLASS(QmitkSegmentationPreferencePage, context)
BERRY_REGISTER_EXTENSION_CLASS(QmitkSegmentationUtilitiesView, context)
+ BERRY_REGISTER_EXTENSION_CLASS(QmitkAutocropAction, context)
+ BERRY_REGISTER_EXTENSION_CLASS(QmitkAutocropLabelSetImageAction, context)
+ BERRY_REGISTER_EXTENSION_CLASS(QmitkCreatePolygonModelAction, context)
+ BERRY_REGISTER_EXTENSION_CLASS(QmitkLoadMultiLabelPresetAction, context)
+ BERRY_REGISTER_EXTENSION_CLASS(QmitkSaveMultiLabelPresetAction, context)
this->m_context = context;
}
void PluginActivator::stop(ctkPluginContext *)
{
this->m_context = nullptr;
}
PluginActivator* PluginActivator::getDefault()
{
return m_Instance;
}
ctkPluginContext*PluginActivator::getContext()
{
return m_context;
}
diff --git a/SuperBuild.cmake b/SuperBuild.cmake
index d4fddd04f6..d3fee6b98c 100644
--- a/SuperBuild.cmake
+++ b/SuperBuild.cmake
@@ -1,498 +1,502 @@
#-----------------------------------------------------------------------------
# Convenient macro allowing to download a file
#-----------------------------------------------------------------------------
if(NOT MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL)
set(MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL http://mitk.org/download/thirdparty)
endif()
macro(downloadFile url dest)
file(DOWNLOAD ${url} ${dest} STATUS status)
list(GET status 0 error_code)
list(GET status 1 error_msg)
if(error_code)
message(FATAL_ERROR "error: Failed to download ${url} - ${error_msg}")
endif()
endmacro()
#-----------------------------------------------------------------------------
# MITK Prerequisites
#-----------------------------------------------------------------------------
if(UNIX AND NOT APPLE)
include(mitkFunctionCheckPackageHeader)
# Check for libxt-dev
mitkFunctionCheckPackageHeader(StringDefs.h libxt-dev /usr/include/X11/)
# Check for libtiff4-dev
mitkFunctionCheckPackageHeader(tiff.h libtiff4-dev)
endif()
# We need a proper patch program. On Linux and MacOS, we assume
# that "patch" is available. On Windows, we download patch.exe
# if not patch program is found.
find_program(PATCH_COMMAND patch)
if((NOT PATCH_COMMAND OR NOT EXISTS ${PATCH_COMMAND}) AND WIN32)
downloadFile(${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/patch.exe
${CMAKE_CURRENT_BINARY_DIR}/patch.exe)
find_program(PATCH_COMMAND patch ${CMAKE_CURRENT_BINARY_DIR})
endif()
if(NOT PATCH_COMMAND)
message(FATAL_ERROR "No patch program found.")
endif()
#-----------------------------------------------------------------------------
# ExternalProjects
#-----------------------------------------------------------------------------
get_property(external_projects GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS)
if(MITK_CTEST_SCRIPT_MODE)
# Write a file containing the list of enabled external project targets.
# This file can be read by a ctest script to separately build projects.
set(SUPERBUILD_TARGETS )
foreach(proj ${external_projects})
if(MITK_USE_${proj})
list(APPEND SUPERBUILD_TARGETS ${proj})
endif()
endforeach()
file(WRITE "${CMAKE_BINARY_DIR}/SuperBuildTargets.cmake" "set(SUPERBUILD_TARGETS ${SUPERBUILD_TARGETS})")
endif()
# A list of "nice" external projects, playing well together with CMake
set(nice_external_projects ${external_projects})
list(REMOVE_ITEM nice_external_projects Boost)
foreach(proj ${nice_external_projects})
if(MITK_USE_${proj})
set(EXTERNAL_${proj}_DIR "${${proj}_DIR}" CACHE PATH "Path to ${proj} build directory")
mark_as_advanced(EXTERNAL_${proj}_DIR)
if(EXTERNAL_${proj}_DIR)
set(${proj}_DIR ${EXTERNAL_${proj}_DIR})
endif()
endif()
endforeach()
set(EXTERNAL_BOOST_ROOT "${BOOST_ROOT}" CACHE PATH "Path to Boost directory")
mark_as_advanced(EXTERNAL_BOOST_ROOT)
if(EXTERNAL_BOOST_ROOT)
set(BOOST_ROOT ${EXTERNAL_BOOST_ROOT})
endif()
if(BUILD_TESTING)
set(EXTERNAL_MITK_DATA_DIR "${MITK_DATA_DIR}" CACHE PATH "Path to the MITK data directory")
mark_as_advanced(EXTERNAL_MITK_DATA_DIR)
if(EXTERNAL_MITK_DATA_DIR)
set(MITK_DATA_DIR ${EXTERNAL_MITK_DATA_DIR})
endif()
endif()
#-----------------------------------------------------------------------------
# External project settings
#-----------------------------------------------------------------------------
include(ExternalProject)
include(mitkMacroQueryCustomEPVars)
include(mitkFunctionInstallExternalCMakeProject)
include(mitkFunctionCleanExternalProject)
option(MITK_AUTOCLEAN_EXTERNAL_PROJECTS "Experimental: Clean external project builds if updated" ON)
set(ep_prefix "${CMAKE_BINARY_DIR}/ep")
set_property(DIRECTORY PROPERTY EP_PREFIX ${ep_prefix})
# Compute -G arg for configuring external projects with the same CMake generator:
if(CMAKE_EXTRA_GENERATOR)
set(gen "${CMAKE_EXTRA_GENERATOR} - ${CMAKE_GENERATOR}")
else()
set(gen "${CMAKE_GENERATOR}")
endif()
set(gen_platform ${CMAKE_GENERATOR_PLATFORM})
# Use this value where semi-colons are needed in ep_add args:
set(sep "^^")
##
if(MSVC_VERSION)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /bigobj /MP")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj /MP")
endif()
# This is a workaround for passing linker flags
# actually down to the linker invocation
set(_cmake_required_flags_orig ${CMAKE_REQUIRED_FLAGS})
set(CMAKE_REQUIRED_FLAGS "-Wl,-rpath")
mitkFunctionCheckCompilerFlags(${CMAKE_REQUIRED_FLAGS} _has_rpath_flag)
set(CMAKE_REQUIRED_FLAGS ${_cmake_required_flags_orig})
set(_install_rpath_linkflag )
if(_has_rpath_flag)
if(APPLE)
set(_install_rpath_linkflag "-Wl,-rpath,@loader_path/../lib")
else()
set(_install_rpath_linkflag "-Wl,-rpath='$ORIGIN/../lib'")
endif()
endif()
set(_install_rpath)
if(APPLE)
set(_install_rpath "@loader_path/../lib")
elseif(UNIX)
# this work for libraries as well as executables
set(_install_rpath "\$ORIGIN/../lib")
endif()
set(ep_common_args
-DCMAKE_POLICY_DEFAULT_CMP0091:STRING=OLD
-DCMAKE_CXX_EXTENSIONS:STRING=${CMAKE_CXX_EXTENSIONS}
-DCMAKE_CXX_STANDARD:STRING=${CMAKE_CXX_STANDARD}
-DCMAKE_CXX_STANDARD_REQUIRED:BOOL=${CMAKE_CXX_STANDARD_REQUIRED}
-DCMAKE_MACOSX_RPATH:BOOL=TRUE
"-DCMAKE_INSTALL_RPATH:STRING=${_install_rpath}"
-DBUILD_TESTING:BOOL=OFF
-DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
-DBUILD_SHARED_LIBS:BOOL=ON
-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
-DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER}
-DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER}
-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS}
- "-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS} ${MITK_CXX14_FLAG}"
+ "-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS} ${MITK_CXX${MITK_CXX_STANDARD}_FLAG}"
#debug flags
-DCMAKE_CXX_FLAGS_DEBUG:STRING=${CMAKE_CXX_FLAGS_DEBUG}
-DCMAKE_C_FLAGS_DEBUG:STRING=${CMAKE_C_FLAGS_DEBUG}
#release flags
-DCMAKE_CXX_FLAGS_RELEASE:STRING=${CMAKE_CXX_FLAGS_RELEASE}
-DCMAKE_C_FLAGS_RELEASE:STRING=${CMAKE_C_FLAGS_RELEASE}
#relwithdebinfo
-DCMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=${CMAKE_CXX_FLAGS_RELWITHDEBINFO}
-DCMAKE_C_FLAGS_RELWITHDEBINFO:STRING=${CMAKE_C_FLAGS_RELWITHDEBINFO}
#link flags
-DCMAKE_EXE_LINKER_FLAGS:STRING=${CMAKE_EXE_LINKER_FLAGS}
-DCMAKE_SHARED_LINKER_FLAGS:STRING=${CMAKE_SHARED_LINKER_FLAGS}
-DCMAKE_MODULE_LINKER_FLAGS:STRING=${CMAKE_MODULE_LINKER_FLAGS}
)
if(MSVC_VERSION)
list(APPEND ep_common_args
-DCMAKE_DEBUG_POSTFIX:STRING=d
)
set(DCMTK_CMAKE_DEBUG_POSTFIX d)
endif()
set(ep_common_cache_args
)
set(ep_common_cache_default_args
"-DCMAKE_PREFIX_PATH:PATH=<INSTALL_DIR>;${CMAKE_PREFIX_PATH}"
"-DCMAKE_INCLUDE_PATH:PATH=${CMAKE_INCLUDE_PATH}"
"-DCMAKE_LIBRARY_PATH:PATH=${CMAKE_LIBRARY_PATH}"
)
# Pass the CMAKE_OSX variables to external projects
if(APPLE)
set(MAC_OSX_ARCHITECTURE_ARGS
-DCMAKE_OSX_ARCHITECTURES:PATH=${CMAKE_OSX_ARCHITECTURES}
-DCMAKE_OSX_DEPLOYMENT_TARGET:PATH=${CMAKE_OSX_DEPLOYMENT_TARGET}
-DCMAKE_OSX_SYSROOT:PATH=${CMAKE_OSX_SYSROOT}
)
set(ep_common_args
${MAC_OSX_ARCHITECTURE_ARGS}
${ep_common_args}
)
endif()
set(mitk_superbuild_ep_args)
set(mitk_depends )
# Include external projects
include(CMakeExternals/MITKData.cmake)
foreach(p ${external_projects})
set(p_hash "")
set(p_file "${CMAKE_SOURCE_DIR}/CMakeExternals/${p}.cmake")
if(EXISTS ${p_file})
file(MD5 ${p_file} p_hash)
else()
foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS})
set(MITK_CMAKE_EXTERNALS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMakeExternals")
set(p_file "${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/${p}.cmake")
if(EXISTS "${p_file}")
file(MD5 "${p_file}" p_hash)
break()
endif()
endforeach()
endif()
if(p_hash)
set(p_hash_file "${ep_prefix}/tmp/${p}-hash.txt")
if(MITK_AUTOCLEAN_EXTERNAL_PROJECTS)
if(EXISTS "${p_hash_file}")
file(READ "${p_hash_file}" p_prev_hash)
if(NOT p_hash STREQUAL p_prev_hash)
mitkCleanExternalProject(${p})
endif()
endif()
endif()
file(WRITE "${p_hash_file}" ${p_hash})
endif()
include("${p_file}" OPTIONAL)
list(APPEND mitk_superbuild_ep_args
-DMITK_USE_${p}:BOOL=${MITK_USE_${p}}
)
get_property(_package GLOBAL PROPERTY MITK_${p}_PACKAGE)
if(_package)
list(APPEND mitk_superbuild_ep_args -D${p}_DIR:PATH=${${p}_DIR})
endif()
list(APPEND mitk_depends ${${p}_DEPENDS})
endforeach()
if (SWIG_EXECUTABLE)
list(APPEND mitk_superbuild_ep_args -DSWIG_EXECUTABLE=${SWIG_EXECUTABLE})
endif()
#-----------------------------------------------------------------------------
# Set superbuild boolean args
#-----------------------------------------------------------------------------
set(mitk_cmake_boolean_args
BUILD_SHARED_LIBS
WITH_COVERAGE
BUILD_TESTING
MITK_BUILD_ALL_PLUGINS
MITK_BUILD_ALL_APPS
MITK_BUILD_EXAMPLES
MITK_USE_Qt5
MITK_USE_SYSTEM_Boost
MITK_USE_BLUEBERRY
MITK_USE_OpenCL
MITK_USE_OpenMP
)
#-----------------------------------------------------------------------------
# Create the final variable containing superbuild boolean args
#-----------------------------------------------------------------------------
set(mitk_superbuild_boolean_args)
foreach(mitk_cmake_arg ${mitk_cmake_boolean_args})
list(APPEND mitk_superbuild_boolean_args -D${mitk_cmake_arg}:BOOL=${${mitk_cmake_arg}})
endforeach()
if(MITK_BUILD_ALL_PLUGINS)
list(APPEND mitk_superbuild_boolean_args -DBLUEBERRY_BUILD_ALL_PLUGINS:BOOL=ON)
endif()
#-----------------------------------------------------------------------------
# MITK Utilities
#-----------------------------------------------------------------------------
set(proj MITK-Utilities)
ExternalProject_Add(${proj}
DOWNLOAD_COMMAND ""
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
DEPENDS
${mitk_depends}
)
#-----------------------------------------------------------------------------
# Additional MITK CXX/C Flags
#-----------------------------------------------------------------------------
set(MITK_ADDITIONAL_C_FLAGS "" CACHE STRING "Additional C Flags for MITK")
set(MITK_ADDITIONAL_C_FLAGS_RELEASE "" CACHE STRING "Additional Release C Flags for MITK")
set(MITK_ADDITIONAL_C_FLAGS_DEBUG "" CACHE STRING "Additional Debug C Flags for MITK")
mark_as_advanced(MITK_ADDITIONAL_C_FLAGS MITK_ADDITIONAL_C_FLAGS_DEBUG MITK_ADDITIONAL_C_FLAGS_RELEASE)
set(MITK_ADDITIONAL_CXX_FLAGS "" CACHE STRING "Additional CXX Flags for MITK")
set(MITK_ADDITIONAL_CXX_FLAGS_RELEASE "" CACHE STRING "Additional Release CXX Flags for MITK")
set(MITK_ADDITIONAL_CXX_FLAGS_DEBUG "" CACHE STRING "Additional Debug CXX Flags for MITK")
mark_as_advanced(MITK_ADDITIONAL_CXX_FLAGS MITK_ADDITIONAL_CXX_FLAGS_DEBUG MITK_ADDITIONAL_CXX_FLAGS_RELEASE)
set(MITK_ADDITIONAL_EXE_LINKER_FLAGS "" CACHE STRING "Additional exe linker flags for MITK")
set(MITK_ADDITIONAL_SHARED_LINKER_FLAGS "" CACHE STRING "Additional shared linker flags for MITK")
set(MITK_ADDITIONAL_MODULE_LINKER_FLAGS "" CACHE STRING "Additional module linker flags for MITK")
mark_as_advanced(MITK_ADDITIONAL_EXE_LINKER_FLAGS MITK_ADDITIONAL_SHARED_LINKER_FLAGS MITK_ADDITIONAL_MODULE_LINKER_FLAGS)
#-----------------------------------------------------------------------------
# MITK Configure
#-----------------------------------------------------------------------------
if(MITK_INITIAL_CACHE_FILE)
set(mitk_initial_cache_arg -C "${MITK_INITIAL_CACHE_FILE}")
endif()
set(mitk_optional_cache_args )
foreach(type RUNTIME ARCHIVE LIBRARY)
if(DEFINED CTK_PLUGIN_${type}_OUTPUT_DIRECTORY)
list(APPEND mitk_optional_cache_args -DCTK_PLUGIN_${type}_OUTPUT_DIRECTORY:PATH=${CTK_PLUGIN_${type}_OUTPUT_DIRECTORY})
endif()
endforeach()
+if(MITK_USE_OpenCV)
+ list(APPEND mitk_optional_cache_args "-DCMAKE_CONFIGURATION_TYPES:STRING=Debug$<SEMICOLON>Release")
+endif()
+
# Optional python variables
if(MITK_USE_Python3)
list(APPEND mitk_optional_cache_args
-DMITK_USE_Python3:BOOL=${MITK_USE_Python3}
"-DPython3_EXECUTABLE:FILEPATH=${Python3_EXECUTABLE}"
"-DPython3_INCLUDE_DIR:PATH=${Python3_INCLUDE_DIRS}"
"-DPython3_LIBRARY:FILEPATH=${Python3_LIBRARY}"
"-DPython3_STDLIB:FILEPATH=${Python3_STDLIB}"
"-DPython3_SITELIB:FILEPATH=${Python3_SITELIB}"
)
endif()
if(OPENSSL_ROOT_DIR)
list(APPEND mitk_optional_cache_args
"-DOPENSSL_ROOT_DIR:PATH=${OPENSSL_ROOT_DIR}"
)
endif()
if(CMAKE_FRAMEWORK_PATH)
list(APPEND mitk_optional_cache_args
"-DCMAKE_FRAMEWORK_PATH:PATH=${CMAKE_FRAMEWORK_PATH}"
)
endif()
if(Eigen_INCLUDE_DIR)
list(APPEND mitk_optional_cache_args
-DEigen_INCLUDE_DIR:PATH=${Eigen_INCLUDE_DIR}
)
endif()
# Optional pass through of Doxygen
if(DOXYGEN_EXECUTABLE)
list(APPEND mitk_optional_cache_args
-DDOXYGEN_EXECUTABLE:FILEPATH=${DOXYGEN_EXECUTABLE}
)
endif()
if(MITK_DOXYGEN_BUILD_ALWAYS)
list(APPEND mitk_optional_cache_args
-DMITK_DOXYGEN_BUILD_ALWAYS:BOOL=${MITK_DOXYGEN_BUILD_ALWAYS}
)
endif()
set(proj MITK-Configure)
ExternalProject_Add(${proj}
LIST_SEPARATOR ${sep}
DOWNLOAD_COMMAND ""
CMAKE_GENERATOR ${gen}
CMAKE_GENERATOR_PLATFORM ${gen_platform}
CMAKE_CACHE_ARGS
# --------------- Build options ----------------
-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX}
-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
"-DCMAKE_PREFIX_PATH:PATH=${ep_prefix};${CMAKE_PREFIX_PATH}"
"-DCMAKE_LIBRARY_PATH:PATH=${CMAKE_LIBRARY_PATH}"
"-DCMAKE_INCLUDE_PATH:PATH=${CMAKE_INCLUDE_PATH}"
# --------------- Compile options ----------------
-DCMAKE_CXX_EXTENSIONS:STRING=${CMAKE_CXX_EXTENSIONS}
-DCMAKE_CXX_STANDARD:STRING=${CMAKE_CXX_STANDARD}
-DCMAKE_CXX_STANDARD_REQUIRED:BOOL=${CMAKE_CXX_STANDARD_REQUIRED}
-DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER}
-DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER}
"-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS} ${MITK_ADDITIONAL_C_FLAGS}"
"-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS} ${MITK_ADDITIONAL_CXX_FLAGS}"
# debug flags
"-DCMAKE_CXX_FLAGS_DEBUG:STRING=${CMAKE_CXX_FLAGS_DEBUG} ${MITK_ADDITIONAL_CXX_FLAGS_DEBUG}"
"-DCMAKE_C_FLAGS_DEBUG:STRING=${CMAKE_C_FLAGS_DEBUG} ${MITK_ADDITIONAL_C_FLAGS_DEBUG}"
# release flags
"-DCMAKE_CXX_FLAGS_RELEASE:STRING=${CMAKE_CXX_FLAGS_RELEASE} ${MITK_ADDITIONAL_CXX_FLAGS_RELEASE}"
"-DCMAKE_C_FLAGS_RELEASE:STRING=${CMAKE_C_FLAGS_RELEASE} ${MITK_ADDITIONAL_C_FLAGS_RELEASE}"
# relwithdebinfo
-DCMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=${CMAKE_CXX_FLAGS_RELWITHDEBINFO}
-DCMAKE_C_FLAGS_RELWITHDEBINFO:STRING=${CMAKE_C_FLAGS_RELWITHDEBINFO}
# link flags
"-DCMAKE_EXE_LINKER_FLAGS:STRING=${CMAKE_EXE_LINKER_FLAGS} ${MITK_ADDITIONAL_EXE_LINKER_FLAGS}"
"-DCMAKE_SHARED_LINKER_FLAGS:STRING=${CMAKE_SHARED_LINKER_FLAGS} ${MITK_ADDITIONAL_SHARED_LINKER_FLAGS}"
"-DCMAKE_MODULE_LINKER_FLAGS:STRING=${CMAKE_MODULE_LINKER_FLAGS} ${MITK_ADDITIONAL_MODULE_LINKER_FLAGS}"
# Output directories
-DMITK_CMAKE_LIBRARY_OUTPUT_DIRECTORY:PATH=${MITK_CMAKE_LIBRARY_OUTPUT_DIRECTORY}
-DMITK_CMAKE_RUNTIME_OUTPUT_DIRECTORY:PATH=${MITK_CMAKE_RUNTIME_OUTPUT_DIRECTORY}
-DMITK_CMAKE_ARCHIVE_OUTPUT_DIRECTORY:PATH=${MITK_CMAKE_ARCHIVE_OUTPUT_DIRECTORY}
# ------------- Boolean build options --------------
${mitk_superbuild_boolean_args}
${mitk_optional_cache_args}
-DMITK_USE_SUPERBUILD:BOOL=OFF
-DMITK_BUILD_CONFIGURATION:STRING=${MITK_BUILD_CONFIGURATION}
-DMITK_FAST_TESTING:BOOL=${MITK_FAST_TESTING}
-DMITK_XVFB_TESTING:BOOL=${MITK_XVFB_TESTING}
-DMITK_XVFB_TESTING_COMMAND:STRING=${MITK_XVFB_TESTING_COMMAND}
-DCTEST_USE_LAUNCHERS:BOOL=${CTEST_USE_LAUNCHERS}
# ----------------- Miscellaneous ---------------
-DCMAKE_LIBRARY_PATH:PATH=${CMAKE_LIBRARY_PATH}
-DCMAKE_INCLUDE_PATH:PATH=${CMAKE_INCLUDE_PATH}
-DMITK_CTEST_SCRIPT_MODE:STRING=${MITK_CTEST_SCRIPT_MODE}
-DMITK_SUPERBUILD_BINARY_DIR:PATH=${MITK_BINARY_DIR}
-DMITK_MODULES_TO_BUILD:INTERNAL=${MITK_MODULES_TO_BUILD}
-DMITK_WHITELIST:STRING=${MITK_WHITELIST}
-DMITK_WHITELISTS_EXTERNAL_PATH:STRING=${MITK_WHITELISTS_EXTERNAL_PATH}
-DMITK_WHITELISTS_INTERNAL_PATH:STRING=${MITK_WHITELISTS_INTERNAL_PATH}
-DMITK_EXTENSION_DIRS:STRING=${MITK_EXTENSION_DIRS}
-DMITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES:STRING=${MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES}
-DMITK_ACCESSBYITK_FLOATING_PIXEL_TYPES:STRING=${MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES}
-DMITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES:STRING=${MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES}
-DMITK_ACCESSBYITK_VECTOR_PIXEL_TYPES:STRING=${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}
-DMITK_ACCESSBYITK_DIMENSIONS:STRING=${MITK_ACCESSBYITK_DIMENSIONS}
-DMITK_CUSTOM_REVISION_DESC:STRING=${MITK_CUSTOM_REVISION_DESC}
# --------------- External project options ---------------
-DMITK_DATA_DIR:PATH=${MITK_DATA_DIR}
-DMITK_EXTERNAL_PROJECT_PREFIX:PATH=${ep_prefix}
-DCppMicroServices_DIR:PATH=${CppMicroServices_DIR}
-DDCMTK_CMAKE_DEBUG_POSTFIX:STRING=${DCMTK_CMAKE_DEBUG_POSTFIX}
-DBOOST_ROOT:PATH=${BOOST_ROOT}
-DBOOST_LIBRARYDIR:PATH=${BOOST_LIBRARYDIR}
-DMITK_USE_Boost_LIBRARIES:STRING=${MITK_USE_Boost_LIBRARIES}
-DQt5_DIR:PATH=${Qt5_DIR}
CMAKE_ARGS
${mitk_initial_cache_arg}
${MAC_OSX_ARCHITECTURE_ARGS}
${mitk_superbuild_ep_args}
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}
BINARY_DIR ${CMAKE_BINARY_DIR}/MITK-build
BUILD_COMMAND ""
INSTALL_COMMAND ""
DEPENDS
MITK-Utilities
)
mitkFunctionInstallExternalCMakeProject(${proj})
#-----------------------------------------------------------------------------
# MITK
#-----------------------------------------------------------------------------
if(CMAKE_GENERATOR MATCHES ".*Makefiles.*")
set(mitk_build_cmd "$(MAKE)")
else()
set(mitk_build_cmd ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/MITK-build --config ${CMAKE_CFG_INTDIR})
endif()
if(NOT DEFINED SUPERBUILD_EXCLUDE_MITKBUILD_TARGET OR NOT SUPERBUILD_EXCLUDE_MITKBUILD_TARGET)
set(MITKBUILD_TARGET_ALL_OPTION "ALL")
else()
set(MITKBUILD_TARGET_ALL_OPTION "")
endif()
add_custom_target(MITK-build ${MITKBUILD_TARGET_ALL_OPTION}
COMMAND ${mitk_build_cmd}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/MITK-build
DEPENDS MITK-Configure
)
#-----------------------------------------------------------------------------
# Custom target allowing to drive the build of the MITK project itself
#-----------------------------------------------------------------------------
add_custom_target(MITK
COMMAND ${mitk_build_cmd}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/MITK-build
)

File Metadata

Mime Type
application/octet-stream
Expires
Fri, May 10, 8:27 AM (1 d, 23 h)
Storage Engine
chunks
Storage Format
Chunks
Storage Handle
SygZshDYspR3
Default Alt Text
(4 MB)

Event Timeline